From 1893d4c90b7271173491ca5a3bfcdf5f86cf42ec Mon Sep 17 00:00:00 2001 From: Victor Date: Fri, 15 Dec 2023 21:26:29 +0100 Subject: [PATCH 01/20] mvp --- .gitignore | 2 + Cargo.lock | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 9 ++ src/main.rs | 139 +++++++++++++++++++++++++++++++ 4 files changed, 380 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c403c34 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.idea/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c17ed80 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,230 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "eyre" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gnome-autounlock-keyring" +version = "0.1.0" +dependencies = [ + "color-eyre", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bc6102e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "gnome-autounlock-keyring" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +color-eyre = "0.6.2" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..870ca4b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,139 @@ +use color_eyre::eyre::{bail, eyre, WrapErr}; +use std::env; +use std::io::{Read, Write}; +use std::os::unix::net::UnixStream; +use std::path::{Path, PathBuf}; + +fn get_control_socket() -> Option { + let gnome_var = env::var("GNOME_KEYRING_CONTROL") + .ok() + .map(|el| PathBuf::from(el).join("control")) + .and_then(|el| el.exists().then_some(el)); + + let xdg_var = env::var("XDG_RUNTIME_DIR") + .ok() + .map(|el| PathBuf::from(el).join("keyring").join("control")) + .and_then(|el| el.exists().then_some(el)); + + gnome_var.or(xdg_var) +} + +enum ControlOp { + Initialize = 0, + Unlock = 1, + Change = 2, + Quit = 4, +} + +impl ControlOp { + fn to_bytes(self) -> [u8; 4] { + (self as u32).to_be_bytes() + } +} + +#[derive(Debug)] +enum ControlResult { + Ok = 0, + Denied = 1, + Failed = 2, + NoDaemon = 3, +} + +impl ControlResult { + fn from_u32(n: u32) -> Option { + match n { + 0 => Some(ControlResult::Ok), + 1 => Some(ControlResult::Denied), + 2 => Some(ControlResult::Failed), + 3 => Some(ControlResult::NoDaemon), + _ => None, + } + } +} + +fn unlock_keyring(password: &str) -> color_eyre::Result { + let socket = get_control_socket() + .ok_or_else(|| eyre!("Could not find gnome keyring control socket path"))?; + let mut stream = UnixStream::connect(socket) + .wrap_err("Could not connect to the gnome keyring unix socket")?; + + let ret = stream + .write(&[0]) + .wrap_err("could not write credential byte")?; + + if ret != 1 { + bail!("writing cred byte failed") + } + + // oplen is + // 8 = packet size + op code + // 4 size of length of pw byte + let oplen: u32 = 8 + 4 + password.len() as u32; + + // write length + let ret = stream + .write(&oplen.to_be_bytes()) + .wrap_err("could not write oplen")?; + + if ret != 4 { + bail!("writing oplen failed") + } + + // write unlock + let ret = stream + .write(&ControlOp::Unlock.to_bytes()) + .wrap_err("could not write unlock")?; + + if ret != 4 { + bail!("writing unlock failed") + } + + // write pw len + let ret = stream + .write(&(password.len() as u32).to_be_bytes()) + .wrap_err("could not write password length")?; + + if ret != 4 { + bail!("writing pwlen failed") + } + + let mut pw_buf = password.as_bytes(); + + while !pw_buf.is_empty() { + let ret = stream.write(pw_buf).wrap_err("writing password failed")?; + pw_buf = &pw_buf[ret..] + } + + let mut buf = [0; 4]; + let val = stream + .read(&mut buf) + .wrap_err("could not read response length")?; + if val != 4 { + bail!("invalid response length length") + } + + let len = u32::from_be_bytes(buf); + if len != 8 { + bail!("invalid response length"); + } + + let val = stream.read(&mut buf).wrap_err("could not read response")?; + if val != 4 { + bail!("invalid response length (2)") + } + + let resp = u32::from_be_bytes(buf); + let code = ControlResult::from_u32(resp).ok_or_else(|| eyre!("invalid resp"))?; + + Ok(code) +} + +fn main() -> color_eyre::Result<()> { + color_eyre::install().unwrap(); + + let res = unlock_keyring("example")?; + + dbg!(res); + + Ok(()) +} From cbf4e9d62f0e9e5e63b16dffb4e824f1d6315438 Mon Sep 17 00:00:00 2001 From: Vivian Roest Date: Fri, 15 Dec 2023 21:26:29 +0100 Subject: [PATCH 02/20] mvp --- .gitignore | 2 + Cargo.lock | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 9 ++ src/main.rs | 139 +++++++++++++++++++++++++++++++ 4 files changed, 380 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c403c34 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.idea/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c17ed80 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,230 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "eyre" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gnome-autounlock-keyring" +version = "0.1.0" +dependencies = [ + "color-eyre", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bc6102e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "gnome-autounlock-keyring" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +color-eyre = "0.6.2" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..870ca4b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,139 @@ +use color_eyre::eyre::{bail, eyre, WrapErr}; +use std::env; +use std::io::{Read, Write}; +use std::os::unix::net::UnixStream; +use std::path::{Path, PathBuf}; + +fn get_control_socket() -> Option { + let gnome_var = env::var("GNOME_KEYRING_CONTROL") + .ok() + .map(|el| PathBuf::from(el).join("control")) + .and_then(|el| el.exists().then_some(el)); + + let xdg_var = env::var("XDG_RUNTIME_DIR") + .ok() + .map(|el| PathBuf::from(el).join("keyring").join("control")) + .and_then(|el| el.exists().then_some(el)); + + gnome_var.or(xdg_var) +} + +enum ControlOp { + Initialize = 0, + Unlock = 1, + Change = 2, + Quit = 4, +} + +impl ControlOp { + fn to_bytes(self) -> [u8; 4] { + (self as u32).to_be_bytes() + } +} + +#[derive(Debug)] +enum ControlResult { + Ok = 0, + Denied = 1, + Failed = 2, + NoDaemon = 3, +} + +impl ControlResult { + fn from_u32(n: u32) -> Option { + match n { + 0 => Some(ControlResult::Ok), + 1 => Some(ControlResult::Denied), + 2 => Some(ControlResult::Failed), + 3 => Some(ControlResult::NoDaemon), + _ => None, + } + } +} + +fn unlock_keyring(password: &str) -> color_eyre::Result { + let socket = get_control_socket() + .ok_or_else(|| eyre!("Could not find gnome keyring control socket path"))?; + let mut stream = UnixStream::connect(socket) + .wrap_err("Could not connect to the gnome keyring unix socket")?; + + let ret = stream + .write(&[0]) + .wrap_err("could not write credential byte")?; + + if ret != 1 { + bail!("writing cred byte failed") + } + + // oplen is + // 8 = packet size + op code + // 4 size of length of pw byte + let oplen: u32 = 8 + 4 + password.len() as u32; + + // write length + let ret = stream + .write(&oplen.to_be_bytes()) + .wrap_err("could not write oplen")?; + + if ret != 4 { + bail!("writing oplen failed") + } + + // write unlock + let ret = stream + .write(&ControlOp::Unlock.to_bytes()) + .wrap_err("could not write unlock")?; + + if ret != 4 { + bail!("writing unlock failed") + } + + // write pw len + let ret = stream + .write(&(password.len() as u32).to_be_bytes()) + .wrap_err("could not write password length")?; + + if ret != 4 { + bail!("writing pwlen failed") + } + + let mut pw_buf = password.as_bytes(); + + while !pw_buf.is_empty() { + let ret = stream.write(pw_buf).wrap_err("writing password failed")?; + pw_buf = &pw_buf[ret..] + } + + let mut buf = [0; 4]; + let val = stream + .read(&mut buf) + .wrap_err("could not read response length")?; + if val != 4 { + bail!("invalid response length length") + } + + let len = u32::from_be_bytes(buf); + if len != 8 { + bail!("invalid response length"); + } + + let val = stream.read(&mut buf).wrap_err("could not read response")?; + if val != 4 { + bail!("invalid response length (2)") + } + + let resp = u32::from_be_bytes(buf); + let code = ControlResult::from_u32(resp).ok_or_else(|| eyre!("invalid resp"))?; + + Ok(code) +} + +fn main() -> color_eyre::Result<()> { + color_eyre::install().unwrap(); + + let res = unlock_keyring("example")?; + + dbg!(res); + + Ok(()) +} From 19a7f2016234e21b415b59e91c8a02b0f0947b4d Mon Sep 17 00:00:00 2001 From: Victor Date: Mon, 18 Dec 2023 22:29:13 +0100 Subject: [PATCH 03/20] tpm2 integration --- .envrc | 1 + .gitignore | 1 + Cargo.lock | 1062 +++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 7 +- flake.lock | 60 +++ flake.nix | 46 +++ src/main.rs | 125 +++--- 7 files changed, 1249 insertions(+), 53 deletions(-) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index c403c34..1b91d7b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target .idea/ +.direnv \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c17ed80..dd9bd14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,86 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "backtrace" version = "0.3.69" @@ -32,6 +112,59 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "bindgen" +version = "0.66.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "cc" version = "1.0.83" @@ -41,12 +174,86 @@ dependencies = [ "libc", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "clevis-pin-tpm2" +version = "0.5.3" +dependencies = [ + "anyhow", + "atty", + "base64 0.12.3", + "josekit", + "serde", + "serde_json", + "tpm2-policy", + "tss-esapi", +] + [[package]] name = "color-eyre" version = "0.6.2" @@ -74,6 +281,72 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "deranged" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "enumflags2" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" +dependencies = [ + "enumflags2_derive", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "eyre" version = "0.6.11" @@ -84,43 +357,193 @@ dependencies = [ "once_cell", ] +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "gnome-autounlock-keyring" version = "0.1.0" dependencies = [ + "clap", + "clevis-pin-tpm2", "color-eyre", + "rpassword", + "serde", + "serde_json", ] +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "hostname-validator" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" + [[package]] name = "indenter" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "josekit" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e84ea7acc05b40e2fe6fa02a54b3731323c77e6015c36749f0b10c4dbbc32f" +dependencies = [ + "anyhow", + "base64 0.13.1", + "flate2", + "once_cell", + "openssl", + "regex", + "serde_json", + "thiserror", + "time", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "mbox" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f88d5c34d63aad11aa4321ef55ccb064af58b3ad8091079ae22bf83e5eb75d6" +dependencies = [ + "libc", + "rustc_version", + "stable_deref_trait", +] + [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -130,6 +553,36 @@ dependencies = [ "adler", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.32.1" @@ -139,30 +592,312 @@ dependencies = [ "memchr", ] +[[package]] +name = "oid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c19903c598813dba001b53beeae59bb77ad4892c5c1b9b3500ce4293a0d06c2" +dependencies = [ + "serde", +] + [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-sys" +version = "0.9.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "owo-colors" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pest" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "picky-asn1" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "295eea0f33c16be21e2a98b908fdd4d73c04dd48c8480991b76dbcf0cb58b212" +dependencies = [ + "oid", + "serde", + "serde_bytes", +] + +[[package]] +name = "picky-asn1-der" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df7873a9e36d42dadb393bea5e211fe83d793c172afad5fb4ec846ec582793f" +dependencies = [ + "picky-asn1", + "serde", + "serde_bytes", +] + +[[package]] +name = "picky-asn1-x509" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c5f20f71a68499ff32310f418a6fad8816eac1a2859ed3f0c5c741389dd6208" +dependencies = [ + "base64 0.21.5", + "oid", + "picky-asn1", + "picky-asn1-der", + "serde", +] + [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rpassword" +version = "7.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.48.0", +] + +[[package]] +name = "rtoolbox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -172,6 +907,61 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" + +[[package]] +name = "thiserror" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.7" @@ -182,6 +972,35 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "powerfmt", + "serde", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "tpm2-policy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bd130084fd4b1d87e7d24a1334eb31136e88fea83b0ab08ac05e1d9e033a2a" +dependencies = [ + "base64 0.12.3", + "serde", + "tss-esapi", +] + [[package]] name = "tracing" version = "0.1.40" @@ -223,8 +1042,251 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tss-esapi" +version = "7.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de234df360c349f78ecd33f0816ab3842db635732212b5cfad67f2638336864e" +dependencies = [ + "bitfield", + "enumflags2", + "hostname-validator", + "log", + "mbox", + "num-derive", + "num-traits", + "oid", + "picky-asn1", + "picky-asn1-x509", + "regex", + "serde", + "tss-esapi-sys", + "zeroize", +] + +[[package]] +name = "tss-esapi-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "535cd192581c2ec4d5f82e670b1d3fbba6a23ccce8c85de387642051d7cad5b5" +dependencies = [ + "bindgen", + "pkg-config", + "target-lexicon", +] + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index bc6102e..9020bf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -color-eyre = "0.6.2" \ No newline at end of file +color-eyre = "0.6.2" +serde_json = "1.0.108" +serde = { version = "1.0.193", features = ["derive"] } +clevis-pin-tpm2 = { path = "../clevis-pin-tpm2" } +clap = { version = "4", features = ["derive"] } +rpassword = "7.3.1" \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..66a6297 --- /dev/null +++ b/flake.lock @@ -0,0 +1,60 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1702933230, + "narHash": "sha256-xi8AZ3noIXrgmKLR+ij+CeYFoUTKiQuTLL+aA7FRdRQ=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "4e2c5373180ecd17e41e879420be69dc642a6349", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..ec6ef15 --- /dev/null +++ b/flake.nix @@ -0,0 +1,46 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + inherit (pkgs) stdenv lib; + in { + devShell = pkgs.mkShell { + shellHook = '' + export BINDGEN_EXTRA_CLANG_ARGS="$(< ${stdenv.cc}/nix-support/libc-crt1-cflags) \ + $(< ${stdenv.cc}/nix-support/libc-cflags) \ + $(< ${stdenv.cc}/nix-support/cc-cflags) \ + $(< ${stdenv.cc}/nix-support/libcxx-cxxflags) \ + ${ + lib.optionalString stdenv.cc.isClang + "-idirafter ${stdenv.cc.cc}/lib/clang/${ + lib.getVersion stdenv.cc.cc + }/include" + } \ + ${ + lib.optionalString stdenv.cc.isGNU + "-isystem ${stdenv.cc.cc}/include/c++/${ + lib.getVersion stdenv.cc.cc + } -isystem ${stdenv.cc.cc}/include/c++/${ + lib.getVersion stdenv.cc.cc + }/${stdenv.hostPlatform.config} -idirafter ${stdenv.cc.cc}/lib/gcc/${stdenv.hostPlatform.config}/${ + lib.getVersion stdenv.cc.cc + }/include" + } \ + " + ''; + nativeBuildInputs = with pkgs; [ + llvmPackages.libclang + llvmPackages.libcxxClang + clang + ]; + LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; + buildInputs = with pkgs; [ libclang pkg-config openssl tpm2-tss ]; + }; + }); +} diff --git a/src/main.rs b/src/main.rs index 870ca4b..a332885 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,12 @@ +use clap::{Parser, Subcommand}; +use clevis_pin_tpm2::tpm_objects::TPM2Config; use color_eyre::eyre::{bail, eyre, WrapErr}; use std::env; -use std::io::{Read, Write}; +use std::fs::{read_to_string, File}; +use std::io::{stdin, Read, Write}; use std::os::unix::net::UnixStream; use std::path::{Path, PathBuf}; +use std::process::exit; fn get_control_socket() -> Option { let gnome_var = env::var("GNOME_KEYRING_CONTROL") @@ -31,7 +35,7 @@ impl ControlOp { } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] enum ControlResult { Ok = 0, Denied = 1, @@ -40,100 +44,117 @@ enum ControlResult { } impl ControlResult { - fn from_u32(n: u32) -> Option { - match n { - 0 => Some(ControlResult::Ok), - 1 => Some(ControlResult::Denied), - 2 => Some(ControlResult::Failed), - 3 => Some(ControlResult::NoDaemon), + fn from_bytes(bytes: [u8; 4]) -> Option { + let num = u32::from_be_bytes(bytes); + match num { + 0 => Some(Self::Ok), + 1 => Some(Self::Denied), + 3 => Some(Self::NoDaemon), _ => None, } } } -fn unlock_keyring(password: &str) -> color_eyre::Result { +fn unlock_keyring(password: &[u8]) -> color_eyre::Result { let socket = get_control_socket() .ok_or_else(|| eyre!("Could not find gnome keyring control socket path"))?; let mut stream = UnixStream::connect(socket) .wrap_err("Could not connect to the gnome keyring unix socket")?; - let ret = stream - .write(&[0]) + stream + .write_all(&[0]) .wrap_err("could not write credential byte")?; - if ret != 1 { - bail!("writing cred byte failed") - } - // oplen is // 8 = packet size + op code // 4 size of length of pw byte let oplen: u32 = 8 + 4 + password.len() as u32; // write length - let ret = stream - .write(&oplen.to_be_bytes()) + stream + .write_all(&oplen.to_be_bytes()) .wrap_err("could not write oplen")?; - if ret != 4 { - bail!("writing oplen failed") - } - // write unlock - let ret = stream - .write(&ControlOp::Unlock.to_bytes()) + stream + .write_all(&ControlOp::Unlock.to_bytes()) .wrap_err("could not write unlock")?; - if ret != 4 { - bail!("writing unlock failed") - } - // write pw len - let ret = stream - .write(&(password.len() as u32).to_be_bytes()) + stream + .write_all(&(password.len() as u32).to_be_bytes()) .wrap_err("could not write password length")?; - if ret != 4 { - bail!("writing pwlen failed") - } - - let mut pw_buf = password.as_bytes(); - - while !pw_buf.is_empty() { - let ret = stream.write(pw_buf).wrap_err("writing password failed")?; - pw_buf = &pw_buf[ret..] - } + stream.write_all(password).wrap_err("writing pass failed")?; let mut buf = [0; 4]; - let val = stream - .read(&mut buf) + stream + .read_exact(&mut buf) .wrap_err("could not read response length")?; - if val != 4 { - bail!("invalid response length length") - } let len = u32::from_be_bytes(buf); if len != 8 { bail!("invalid response length"); } - let val = stream.read(&mut buf).wrap_err("could not read response")?; - if val != 4 { - bail!("invalid response length (2)") - } + stream + .read_exact(&mut buf) + .wrap_err("could not read response")?; - let resp = u32::from_be_bytes(buf); - let code = ControlResult::from_u32(resp).ok_or_else(|| eyre!("invalid resp"))?; + let code = ControlResult::from_bytes(buf).ok_or_else(|| eyre!("invalid control result"))?; Ok(code) } +#[derive(Parser)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + Unlock, + Enroll, +} + fn main() -> color_eyre::Result<()> { color_eyre::install().unwrap(); + let cli = Cli::parse(); - let res = unlock_keyring("example")?; + match cli.command { + Commands::Unlock => { + let file = PathBuf::from("/home/vivian/.config/gnome_password.token"); + if file.exists() { + let token = read_to_string(file)?; + let password = clevis_pin_tpm2::perform_decrypt(token.as_bytes()) + .map_err(|err| eyre!("{err:?}"))?; + let res = unlock_keyring(password.as_slice())?; + if res != ControlResult::Ok { + eprintln!("Failed to unlock keyring: {res:?}"); + exit(2); + } + } else { + bail!("password token file not found") + } + println!("Unlocked keyring successfully") + } + Commands::Enroll => { + let password = rpassword::prompt_password("Password: ")?; - dbg!(res); + if unlock_keyring(password.as_bytes())? != ControlResult::Ok { + eprintln!("invalid password"); + exit(3); + } + + let token = + clevis_pin_tpm2::perform_encrypt(TPM2Config::default(), password.as_bytes()) + .map_err(|err| eyre!("{err:?}"))?; + let mut file = File::create("/home/vivian/.config/gnome_password.token")?; + file.write_all(token.as_bytes())?; + println!("Password enrolled successfully") + } + } Ok(()) } From 83a41818425211d4175c9b264f451cf7eb04c5ca Mon Sep 17 00:00:00 2001 From: Vivian Roest Date: Mon, 18 Dec 2023 22:29:13 +0100 Subject: [PATCH 04/20] tpm2 integration --- .envrc | 1 + .gitignore | 1 + Cargo.lock | 1062 +++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 7 +- flake.lock | 60 +++ flake.nix | 46 +++ src/main.rs | 125 +++--- 7 files changed, 1249 insertions(+), 53 deletions(-) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index c403c34..1b91d7b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target .idea/ +.direnv \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c17ed80..dd9bd14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,86 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "backtrace" version = "0.3.69" @@ -32,6 +112,59 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "bindgen" +version = "0.66.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "cc" version = "1.0.83" @@ -41,12 +174,86 @@ dependencies = [ "libc", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "clevis-pin-tpm2" +version = "0.5.3" +dependencies = [ + "anyhow", + "atty", + "base64 0.12.3", + "josekit", + "serde", + "serde_json", + "tpm2-policy", + "tss-esapi", +] + [[package]] name = "color-eyre" version = "0.6.2" @@ -74,6 +281,72 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "deranged" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "enumflags2" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" +dependencies = [ + "enumflags2_derive", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "eyre" version = "0.6.11" @@ -84,43 +357,193 @@ dependencies = [ "once_cell", ] +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "gnome-autounlock-keyring" version = "0.1.0" dependencies = [ + "clap", + "clevis-pin-tpm2", "color-eyre", + "rpassword", + "serde", + "serde_json", ] +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "hostname-validator" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" + [[package]] name = "indenter" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "josekit" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e84ea7acc05b40e2fe6fa02a54b3731323c77e6015c36749f0b10c4dbbc32f" +dependencies = [ + "anyhow", + "base64 0.13.1", + "flate2", + "once_cell", + "openssl", + "regex", + "serde_json", + "thiserror", + "time", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "mbox" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f88d5c34d63aad11aa4321ef55ccb064af58b3ad8091079ae22bf83e5eb75d6" +dependencies = [ + "libc", + "rustc_version", + "stable_deref_trait", +] + [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -130,6 +553,36 @@ dependencies = [ "adler", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.32.1" @@ -139,30 +592,312 @@ dependencies = [ "memchr", ] +[[package]] +name = "oid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c19903c598813dba001b53beeae59bb77ad4892c5c1b9b3500ce4293a0d06c2" +dependencies = [ + "serde", +] + [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-sys" +version = "0.9.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "owo-colors" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pest" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "picky-asn1" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "295eea0f33c16be21e2a98b908fdd4d73c04dd48c8480991b76dbcf0cb58b212" +dependencies = [ + "oid", + "serde", + "serde_bytes", +] + +[[package]] +name = "picky-asn1-der" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df7873a9e36d42dadb393bea5e211fe83d793c172afad5fb4ec846ec582793f" +dependencies = [ + "picky-asn1", + "serde", + "serde_bytes", +] + +[[package]] +name = "picky-asn1-x509" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c5f20f71a68499ff32310f418a6fad8816eac1a2859ed3f0c5c741389dd6208" +dependencies = [ + "base64 0.21.5", + "oid", + "picky-asn1", + "picky-asn1-der", + "serde", +] + [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rpassword" +version = "7.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.48.0", +] + +[[package]] +name = "rtoolbox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -172,6 +907,61 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" + +[[package]] +name = "thiserror" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.7" @@ -182,6 +972,35 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "powerfmt", + "serde", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "tpm2-policy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bd130084fd4b1d87e7d24a1334eb31136e88fea83b0ab08ac05e1d9e033a2a" +dependencies = [ + "base64 0.12.3", + "serde", + "tss-esapi", +] + [[package]] name = "tracing" version = "0.1.40" @@ -223,8 +1042,251 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tss-esapi" +version = "7.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de234df360c349f78ecd33f0816ab3842db635732212b5cfad67f2638336864e" +dependencies = [ + "bitfield", + "enumflags2", + "hostname-validator", + "log", + "mbox", + "num-derive", + "num-traits", + "oid", + "picky-asn1", + "picky-asn1-x509", + "regex", + "serde", + "tss-esapi-sys", + "zeroize", +] + +[[package]] +name = "tss-esapi-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "535cd192581c2ec4d5f82e670b1d3fbba6a23ccce8c85de387642051d7cad5b5" +dependencies = [ + "bindgen", + "pkg-config", + "target-lexicon", +] + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index bc6102e..9020bf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -color-eyre = "0.6.2" \ No newline at end of file +color-eyre = "0.6.2" +serde_json = "1.0.108" +serde = { version = "1.0.193", features = ["derive"] } +clevis-pin-tpm2 = { path = "../clevis-pin-tpm2" } +clap = { version = "4", features = ["derive"] } +rpassword = "7.3.1" \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..66a6297 --- /dev/null +++ b/flake.lock @@ -0,0 +1,60 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1702933230, + "narHash": "sha256-xi8AZ3noIXrgmKLR+ij+CeYFoUTKiQuTLL+aA7FRdRQ=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "4e2c5373180ecd17e41e879420be69dc642a6349", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..ec6ef15 --- /dev/null +++ b/flake.nix @@ -0,0 +1,46 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + inherit (pkgs) stdenv lib; + in { + devShell = pkgs.mkShell { + shellHook = '' + export BINDGEN_EXTRA_CLANG_ARGS="$(< ${stdenv.cc}/nix-support/libc-crt1-cflags) \ + $(< ${stdenv.cc}/nix-support/libc-cflags) \ + $(< ${stdenv.cc}/nix-support/cc-cflags) \ + $(< ${stdenv.cc}/nix-support/libcxx-cxxflags) \ + ${ + lib.optionalString stdenv.cc.isClang + "-idirafter ${stdenv.cc.cc}/lib/clang/${ + lib.getVersion stdenv.cc.cc + }/include" + } \ + ${ + lib.optionalString stdenv.cc.isGNU + "-isystem ${stdenv.cc.cc}/include/c++/${ + lib.getVersion stdenv.cc.cc + } -isystem ${stdenv.cc.cc}/include/c++/${ + lib.getVersion stdenv.cc.cc + }/${stdenv.hostPlatform.config} -idirafter ${stdenv.cc.cc}/lib/gcc/${stdenv.hostPlatform.config}/${ + lib.getVersion stdenv.cc.cc + }/include" + } \ + " + ''; + nativeBuildInputs = with pkgs; [ + llvmPackages.libclang + llvmPackages.libcxxClang + clang + ]; + LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; + buildInputs = with pkgs; [ libclang pkg-config openssl tpm2-tss ]; + }; + }); +} diff --git a/src/main.rs b/src/main.rs index 870ca4b..a332885 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,12 @@ +use clap::{Parser, Subcommand}; +use clevis_pin_tpm2::tpm_objects::TPM2Config; use color_eyre::eyre::{bail, eyre, WrapErr}; use std::env; -use std::io::{Read, Write}; +use std::fs::{read_to_string, File}; +use std::io::{stdin, Read, Write}; use std::os::unix::net::UnixStream; use std::path::{Path, PathBuf}; +use std::process::exit; fn get_control_socket() -> Option { let gnome_var = env::var("GNOME_KEYRING_CONTROL") @@ -31,7 +35,7 @@ impl ControlOp { } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] enum ControlResult { Ok = 0, Denied = 1, @@ -40,100 +44,117 @@ enum ControlResult { } impl ControlResult { - fn from_u32(n: u32) -> Option { - match n { - 0 => Some(ControlResult::Ok), - 1 => Some(ControlResult::Denied), - 2 => Some(ControlResult::Failed), - 3 => Some(ControlResult::NoDaemon), + fn from_bytes(bytes: [u8; 4]) -> Option { + let num = u32::from_be_bytes(bytes); + match num { + 0 => Some(Self::Ok), + 1 => Some(Self::Denied), + 3 => Some(Self::NoDaemon), _ => None, } } } -fn unlock_keyring(password: &str) -> color_eyre::Result { +fn unlock_keyring(password: &[u8]) -> color_eyre::Result { let socket = get_control_socket() .ok_or_else(|| eyre!("Could not find gnome keyring control socket path"))?; let mut stream = UnixStream::connect(socket) .wrap_err("Could not connect to the gnome keyring unix socket")?; - let ret = stream - .write(&[0]) + stream + .write_all(&[0]) .wrap_err("could not write credential byte")?; - if ret != 1 { - bail!("writing cred byte failed") - } - // oplen is // 8 = packet size + op code // 4 size of length of pw byte let oplen: u32 = 8 + 4 + password.len() as u32; // write length - let ret = stream - .write(&oplen.to_be_bytes()) + stream + .write_all(&oplen.to_be_bytes()) .wrap_err("could not write oplen")?; - if ret != 4 { - bail!("writing oplen failed") - } - // write unlock - let ret = stream - .write(&ControlOp::Unlock.to_bytes()) + stream + .write_all(&ControlOp::Unlock.to_bytes()) .wrap_err("could not write unlock")?; - if ret != 4 { - bail!("writing unlock failed") - } - // write pw len - let ret = stream - .write(&(password.len() as u32).to_be_bytes()) + stream + .write_all(&(password.len() as u32).to_be_bytes()) .wrap_err("could not write password length")?; - if ret != 4 { - bail!("writing pwlen failed") - } - - let mut pw_buf = password.as_bytes(); - - while !pw_buf.is_empty() { - let ret = stream.write(pw_buf).wrap_err("writing password failed")?; - pw_buf = &pw_buf[ret..] - } + stream.write_all(password).wrap_err("writing pass failed")?; let mut buf = [0; 4]; - let val = stream - .read(&mut buf) + stream + .read_exact(&mut buf) .wrap_err("could not read response length")?; - if val != 4 { - bail!("invalid response length length") - } let len = u32::from_be_bytes(buf); if len != 8 { bail!("invalid response length"); } - let val = stream.read(&mut buf).wrap_err("could not read response")?; - if val != 4 { - bail!("invalid response length (2)") - } + stream + .read_exact(&mut buf) + .wrap_err("could not read response")?; - let resp = u32::from_be_bytes(buf); - let code = ControlResult::from_u32(resp).ok_or_else(|| eyre!("invalid resp"))?; + let code = ControlResult::from_bytes(buf).ok_or_else(|| eyre!("invalid control result"))?; Ok(code) } +#[derive(Parser)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + Unlock, + Enroll, +} + fn main() -> color_eyre::Result<()> { color_eyre::install().unwrap(); + let cli = Cli::parse(); - let res = unlock_keyring("example")?; + match cli.command { + Commands::Unlock => { + let file = PathBuf::from("/home/vivian/.config/gnome_password.token"); + if file.exists() { + let token = read_to_string(file)?; + let password = clevis_pin_tpm2::perform_decrypt(token.as_bytes()) + .map_err(|err| eyre!("{err:?}"))?; + let res = unlock_keyring(password.as_slice())?; + if res != ControlResult::Ok { + eprintln!("Failed to unlock keyring: {res:?}"); + exit(2); + } + } else { + bail!("password token file not found") + } + println!("Unlocked keyring successfully") + } + Commands::Enroll => { + let password = rpassword::prompt_password("Password: ")?; - dbg!(res); + if unlock_keyring(password.as_bytes())? != ControlResult::Ok { + eprintln!("invalid password"); + exit(3); + } + + let token = + clevis_pin_tpm2::perform_encrypt(TPM2Config::default(), password.as_bytes()) + .map_err(|err| eyre!("{err:?}"))?; + let mut file = File::create("/home/vivian/.config/gnome_password.token")?; + file.write_all(token.as_bytes())?; + println!("Password enrolled successfully") + } + } Ok(()) } From f114554dd7800bf0dd88a491b85be5aad6a91ad0 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 19 Dec 2023 09:00:07 +0100 Subject: [PATCH 05/20] tpm2 working --- Cargo.lock | 140 ++++++++++------ Cargo.toml | 15 +- flake.nix | 67 +++++--- result | 1 + src/main.rs | 36 ++-- src/tpm/mod.rs | 216 ++++++++++++++++++++++++ src/tpm/tpm_objects.rs | 366 +++++++++++++++++++++++++++++++++++++++++ src/tpm/utils.rs | 110 +++++++++++++ 8 files changed, 862 insertions(+), 89 deletions(-) create mode 120000 result create mode 100644 src/tpm/mod.rs create mode 100644 src/tpm/tpm_objects.rs create mode 100644 src/tpm/utils.rs diff --git a/Cargo.lock b/Cargo.lock index dd9bd14..62d85a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,17 +80,6 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -118,12 +107,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.5" @@ -136,7 +119,7 @@ version = "0.66.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" dependencies = [ - "bitflags", + "bitflags 2.4.1", "cexpr", "clang-sys", "lazy_static", @@ -159,6 +142,12 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.4.1" @@ -240,20 +229,6 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" -[[package]] -name = "clevis-pin-tpm2" -version = "0.5.3" -dependencies = [ - "anyhow", - "atty", - "base64 0.12.3", - "josekit", - "serde", - "serde_json", - "tpm2-policy", - "tss-esapi", -] - [[package]] name = "color-eyre" version = "0.6.2" @@ -305,6 +280,27 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "either" version = "1.9.0" @@ -382,6 +378,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.28.1" @@ -398,12 +405,16 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" name = "gnome-autounlock-keyring" version = "0.1.0" dependencies = [ + "base64 0.21.5", "clap", - "clevis-pin-tpm2", "color-eyre", + "dirs", + "josekit", "rpassword", "serde", "serde_json", + "tpm2-policy", + "tss-esapi", ] [[package]] @@ -418,15 +429,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "home" version = "0.5.9" @@ -466,16 +468,17 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "josekit" -version = "0.7.4" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e84ea7acc05b40e2fe6fa02a54b3731323c77e6015c36749f0b10c4dbbc32f" +checksum = "5754487a088f527b1407df470db8e654e4064dccbbe1fe850e0773721e9962b7" dependencies = [ "anyhow", - "base64 0.13.1", + "base64 0.21.5", "flate2", "once_cell", "openssl", "regex", + "serde", "serde_json", "thiserror", "time", @@ -509,6 +512,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] + [[package]] name = "linux-raw-sys" version = "0.4.12" @@ -613,7 +627,7 @@ version = "0.10.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" dependencies = [ - "bitflags", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -645,6 +659,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "owo-colors" version = "3.5.0" @@ -749,6 +769,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.2" @@ -826,7 +866,7 @@ version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ - "bitflags", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", @@ -1105,6 +1145,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "which" version = "4.4.2" diff --git a/Cargo.toml b/Cargo.toml index 9020bf0..97be2f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,15 @@ edition = "2021" [dependencies] color-eyre = "0.6.2" -serde_json = "1.0.108" -serde = { version = "1.0.193", features = ["derive"] } -clevis-pin-tpm2 = { path = "../clevis-pin-tpm2" } +serde_json = "1" +serde = { version = "1", features = ["derive"] } clap = { version = "4", features = ["derive"] } -rpassword = "7.3.1" \ No newline at end of file +rpassword = "7.3.1" +tss-esapi = { version = "7.2", features = ["generate-bindings"] } +josekit = "0.8.4" +base64 = { version = "0.21.5", features = [] } +tpm2-policy = "0.6.0" +dirs = "5" + +[profile.release] +lto = "thin" diff --git a/flake.nix b/flake.nix index ec6ef15..fdb20f9 100644 --- a/flake.nix +++ b/flake.nix @@ -7,40 +7,55 @@ outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let + cargoToml = (builtins.fromTOML (builtins.readFile ./Cargo.toml)); pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) stdenv lib; - in { - devShell = pkgs.mkShell { - shellHook = '' - export BINDGEN_EXTRA_CLANG_ARGS="$(< ${stdenv.cc}/nix-support/libc-crt1-cflags) \ - $(< ${stdenv.cc}/nix-support/libc-cflags) \ - $(< ${stdenv.cc}/nix-support/cc-cflags) \ - $(< ${stdenv.cc}/nix-support/libcxx-cxxflags) \ - ${ - lib.optionalString stdenv.cc.isClang - "-idirafter ${stdenv.cc.cc}/lib/clang/${ - lib.getVersion stdenv.cc.cc - }/include" - } \ - ${ - lib.optionalString stdenv.cc.isGNU - "-isystem ${stdenv.cc.cc}/include/c++/${ - lib.getVersion stdenv.cc.cc - } -isystem ${stdenv.cc.cc}/include/c++/${ - lib.getVersion stdenv.cc.cc - }/${stdenv.hostPlatform.config} -idirafter ${stdenv.cc.cc}/lib/gcc/${stdenv.hostPlatform.config}/${ - lib.getVersion stdenv.cc.cc - }/include" - } \ - " - ''; + in rec { + packages.default = pkgs.rustPlatform.buildRustPackage { + pname = cargoToml.package.name; + version = cargoToml.package.version; + src = self; + cargoLock.lockFile = ./Cargo.lock; + + doCheck = false; + nativeBuildInputs = with pkgs; [ llvmPackages.libclang llvmPackages.libcxxClang clang + pkg-config ]; LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; - buildInputs = with pkgs; [ libclang pkg-config openssl tpm2-tss ]; + buildInputs = with pkgs; [ openssl tpm2-tss ]; + + preBuild = '' + export BINDGEN_EXTRA_CLANG_ARGS="$(< ${stdenv.cc}/nix-support/libc-crt1-cflags) \ + $(< ${stdenv.cc}/nix-support/libc-cflags) \ + $(< ${stdenv.cc}/nix-support/cc-cflags) \ + $(< ${stdenv.cc}/nix-support/libcxx-cxxflags) \ + ${ + lib.optionalString stdenv.cc.isClang + "-idirafter ${stdenv.cc.cc}/lib/clang/${ + lib.getVersion stdenv.cc.cc + }/include" + } \ + ${ + lib.optionalString stdenv.cc.isGNU + "-isystem ${stdenv.cc.cc}/include/c++/${ + lib.getVersion stdenv.cc.cc + } -isystem ${stdenv.cc.cc}/include/c++/${ + lib.getVersion stdenv.cc.cc + }/${stdenv.hostPlatform.config} -idirafter ${stdenv.cc.cc}/lib/gcc/${stdenv.hostPlatform.config}/${ + lib.getVersion stdenv.cc.cc + }/include" + } \ + " + ''; + }; + + devShell = pkgs.mkShell { + shellHook = "${packages.default.preBuild}"; + inherit (packages.default) nativeBuildInputs buildInputs LIBCLANG_PATH; }; }); } diff --git a/result b/result new file mode 120000 index 0000000..b5c3c34 --- /dev/null +++ b/result @@ -0,0 +1 @@ +/nix/store/bwvjqn0n9xljxk646l0rqj6m0ym221m7-gnome-autounlock-keyring-0.1.0 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index a332885..54bcc66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,14 @@ +mod tpm; + use clap::{Parser, Subcommand}; -use clevis_pin_tpm2::tpm_objects::TPM2Config; use color_eyre::eyre::{bail, eyre, WrapErr}; use std::env; use std::fs::{read_to_string, File}; -use std::io::{stdin, Read, Write}; +use std::io::{Read, Write}; use std::os::unix::net::UnixStream; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::process::exit; +use tpm::tpm_objects::TPM2Config; fn get_control_socket() -> Option { let gnome_var = env::var("GNOME_KEYRING_CONTROL") @@ -22,6 +24,7 @@ fn get_control_socket() -> Option { gnome_var.or(xdg_var) } +#[derive(Debug, Clone, Copy)] enum ControlOp { Initialize = 0, Unlock = 1, @@ -108,13 +111,19 @@ fn unlock_keyring(password: &[u8]) -> color_eyre::Result { #[derive(Parser)] struct Cli { + /// Defaults to CONFIG_DIR/gnome-keyring.tpm2 + #[arg(short, long)] + token_path: Option, + #[command(subcommand)] command: Commands, } #[derive(Subcommand)] enum Commands { + /// Unlock gnome keyring using encrypted password stored in tpm Unlock, + /// Enroll a password into the tpm to use when unlocking Enroll, } @@ -122,13 +131,17 @@ fn main() -> color_eyre::Result<()> { color_eyre::install().unwrap(); let cli = Cli::parse(); + let token_path = cli + .token_path + .or(dirs::config_dir().map(|el| el.join("gnome-keyring.tpm2"))) + .ok_or_else(|| eyre!("Token path not found"))?; + match cli.command { Commands::Unlock => { - let file = PathBuf::from("/home/vivian/.config/gnome_password.token"); - if file.exists() { - let token = read_to_string(file)?; - let password = clevis_pin_tpm2::perform_decrypt(token.as_bytes()) - .map_err(|err| eyre!("{err:?}"))?; + if token_path.exists() { + let token = read_to_string(token_path)?; + let password = + tpm::perform_decrypt(token.as_bytes()).map_err(|err| eyre!("{err:?}"))?; let res = unlock_keyring(password.as_slice())?; if res != ControlResult::Ok { eprintln!("Failed to unlock keyring: {res:?}"); @@ -147,10 +160,9 @@ fn main() -> color_eyre::Result<()> { exit(3); } - let token = - clevis_pin_tpm2::perform_encrypt(TPM2Config::default(), password.as_bytes()) - .map_err(|err| eyre!("{err:?}"))?; - let mut file = File::create("/home/vivian/.config/gnome_password.token")?; + let token = tpm::perform_encrypt(TPM2Config::default(), password.as_bytes()) + .map_err(|err| eyre!("{err:?}"))?; + let mut file = File::create(token_path)?; file.write_all(token.as_bytes())?; println!("Password enrolled successfully") } diff --git a/src/tpm/mod.rs b/src/tpm/mod.rs new file mode 100644 index 0000000..6210eee --- /dev/null +++ b/src/tpm/mod.rs @@ -0,0 +1,216 @@ +// Copyright 2020 Patrick Uiterwijk +// +// Licensed under the MIT license + +use std::convert::{TryFrom, TryInto}; + +use color_eyre::{ + eyre::{bail, Context, ContextCompat, Error}, + Result, +}; +use josekit::jwe::{alg::direct::DirectJweAlgorithm::Dir, enc::A256GCM}; +use serde::{Deserialize, Serialize}; +use tpm2_policy::TPMPolicyStep; +use tpm_objects::TPM2Config; +use tss_esapi::structures::SensitiveData; + +pub mod tpm_objects; +pub mod utils; + +pub fn perform_encrypt(cfg: TPM2Config, input: &[u8]) -> Result { + let key_type = match &cfg.key { + None => "ecc", + Some(key_type) => key_type, + }; + let key_public = tpm_objects::get_key_public(key_type, cfg.get_name_hash_alg())?; + + let mut ctx = utils::get_tpm2_ctx()?; + let key_handle = utils::get_tpm2_primary_key(&mut ctx, key_public)?; + + let policy_runner: TPMPolicyStep = TPMPolicyStep::try_from(&cfg)?; + + let pin_type = match policy_runner { + TPMPolicyStep::NoStep => "tpm2", + TPMPolicyStep::PCRs(_, _, _) => "tpm2", + _ => "tpm2plus", + }; + + let (_, policy_digest) = policy_runner.send_policy(&mut ctx, true)?; + + let mut jwk = josekit::jwk::Jwk::generate_oct_key(32).context("Error generating random JWK")?; + jwk.set_key_operations(vec!["encrypt", "decrypt"]); + let jwk_str = serde_json::to_string(&jwk.as_ref())?; + + let public = tpm_objects::create_tpm2b_public_sealed_object(policy_digest)?.try_into()?; + let jwk_str = SensitiveData::try_from(jwk_str.as_bytes().to_vec())?; + let jwk_result = ctx.execute_with_nullauth_session(|ctx| { + ctx.create(key_handle, public, None, Some(jwk_str), None, None) + })?; + + let jwk_priv = tpm_objects::get_tpm2b_private(jwk_result.out_private.into())?; + + let jwk_pub = tpm_objects::get_tpm2b_public(jwk_result.out_public.try_into()?)?; + + let private_hdr = ClevisInner { + pin: pin_type.to_string(), + tpm2: Tpm2Inner { + hash: cfg.hash.as_ref().unwrap_or(&"sha256".to_string()).clone(), + key: key_type.to_string(), + jwk_pub, + jwk_priv, + pcr_bank: cfg.pcr_bank.clone(), + pcr_ids: cfg.get_pcr_ids_str(), + policy_pubkey_path: cfg.policy_pubkey_path, + policy_ref: cfg.policy_ref, + policy_path: cfg.policy_path, + }, + }; + + let mut hdr = josekit::jwe::JweHeader::new(); + hdr.set_algorithm(Dir.name()); + hdr.set_content_encryption(A256GCM.name()); + hdr.set_claim( + "clevis", + Some(serde_json::value::to_value(private_hdr).context("Error serializing private header")?), + ) + .context("Error adding clevis claim")?; + + let encrypter = Dir + .encrypter_from_jwk(&jwk) + .context("Error creating direct encrypter")?; + let jwe_token = josekit::jwe::serialize_compact(input, &hdr, &encrypter) + .context("Error serializing JWE token")?; + + Ok(jwe_token) +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +struct Tpm2Inner { + hash: String, + #[serde( + deserialize_with = "utils::deserialize_as_base64_url_no_pad", + serialize_with = "utils::serialize_as_base64_url_no_pad" + )] + jwk_priv: Vec, + #[serde( + deserialize_with = "utils::deserialize_as_base64_url_no_pad", + serialize_with = "utils::serialize_as_base64_url_no_pad" + )] + jwk_pub: Vec, + key: String, + + // PCR Binding may be specified, may not + #[serde(skip_serializing_if = "Option::is_none")] + pcr_bank: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pcr_ids: Option, + + // Public key (in PEM format) for a wildcard policy that's OR'd with the PCR one + #[serde(skip_serializing_if = "Option::is_none")] + policy_pubkey_path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + policy_ref: Option, + #[serde(skip_serializing_if = "Option::is_none")] + policy_path: Option, +} + +impl Tpm2Inner { + fn get_pcr_ids(&self) -> Option> { + Some( + self.pcr_ids + .as_ref()? + .split(',') + .map(|x| x.parse::().unwrap()) + .collect(), + ) + } +} + +impl TryFrom<&Tpm2Inner> for TPMPolicyStep { + type Error = Error; + + fn try_from(cfg: &Tpm2Inner) -> Result { + if cfg.pcr_ids.is_some() && cfg.policy_pubkey_path.is_some() { + Ok(TPMPolicyStep::Or([ + Box::new(TPMPolicyStep::PCRs( + utils::get_hash_alg_from_name(cfg.pcr_bank.as_ref()), + cfg.get_pcr_ids().unwrap(), + Box::new(TPMPolicyStep::NoStep), + )), + Box::new(utils::get_authorized_policy_step( + cfg.policy_pubkey_path.as_ref().unwrap(), + &cfg.policy_path, + &cfg.policy_ref, + )?), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + ])) + } else if cfg.pcr_ids.is_some() { + Ok(TPMPolicyStep::PCRs( + utils::get_hash_alg_from_name(cfg.pcr_bank.as_ref()), + cfg.get_pcr_ids().unwrap(), + Box::new(TPMPolicyStep::NoStep), + )) + } else if cfg.policy_pubkey_path.is_some() { + utils::get_authorized_policy_step( + cfg.policy_pubkey_path.as_ref().unwrap(), + &cfg.policy_path, + &cfg.policy_ref, + ) + } else { + Ok(TPMPolicyStep::NoStep) + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +struct ClevisInner { + pin: String, + tpm2: Tpm2Inner, +} + +pub fn perform_decrypt(input: &[u8]) -> Result> { + let input = String::from_utf8(input.to_vec()).context("Error reading input")?; + let hdr = josekit::jwt::decode_header(&input).context("Error decoding header")?; + let hdr_clevis = hdr.claim("clevis").context("Error getting clevis claim")?; + let hdr_clevis: ClevisInner = + serde_json::from_value(hdr_clevis.clone()).context("Error deserializing clevis header")?; + + if hdr_clevis.pin != "tpm2" && hdr_clevis.pin != "tpm2plus" { + bail!("JWE pin mismatch"); + } + + let jwkpub = tpm_objects::build_tpm2b_public(&hdr_clevis.tpm2.jwk_pub)?.try_into()?; + let jwkpriv = tpm_objects::build_tpm2b_private(&hdr_clevis.tpm2.jwk_priv)?; + + let policy = TPMPolicyStep::try_from(&hdr_clevis.tpm2)?; + + let name_alg = utils::get_hash_alg_from_name(Some(&hdr_clevis.tpm2.hash)); + let key_public = tpm_objects::get_key_public(hdr_clevis.tpm2.key.as_str(), name_alg)?; + + let mut ctx = utils::get_tpm2_ctx()?; + let key_handle = utils::get_tpm2_primary_key(&mut ctx, key_public)?; + + let key = + ctx.execute_with_nullauth_session(|ctx| ctx.load(key_handle, jwkpriv.try_into()?, jwkpub))?; + + let (policy_session, _) = policy.send_policy(&mut ctx, false)?; + + let unsealed = ctx.execute_with_session(policy_session, |ctx| ctx.unseal(key.into()))?; + let unsealed = &unsealed.value(); + let mut jwk = josekit::jwk::Jwk::from_bytes(unsealed).context("Error unmarshaling JWK")?; + jwk.set_parameter("alg", None) + .context("Error removing the alg parameter")?; + let decrypter = Dir + .decrypter_from_jwk(&jwk) + .context("Error creating decrypter")?; + + let (payload, _) = + josekit::jwe::deserialize_compact(&input, &decrypter).context("Error decrypting JWE")?; + + Ok(payload) +} diff --git a/src/tpm/tpm_objects.rs b/src/tpm/tpm_objects.rs new file mode 100644 index 0000000..5d93c75 --- /dev/null +++ b/src/tpm/tpm_objects.rs @@ -0,0 +1,366 @@ +use color_eyre::eyre::{bail, eyre, Context, Error}; +use std::convert::TryFrom; + +use super::utils::get_authorized_policy_step; +use color_eyre::Result; +use serde::{Deserialize, Serialize}; +use tpm2_policy::TPMPolicyStep; +use tss_esapi::{ + attributes::object::ObjectAttributesBuilder, + constants::tss as tss_constants, + interface_types::{ + algorithm::{HashingAlgorithm, PublicAlgorithm}, + ecc::EccCurve, + }, + structures::{Digest, Public, SymmetricDefinitionObject}, +}; + +#[derive(Serialize, Deserialize, Default, std::fmt::Debug)] +pub struct TPM2Config { + pub hash: Option, + pub key: Option, + pub pcr_bank: Option, + // PCR IDs can be passed in as comma-separated string or json array + pub pcr_ids: Option, + pub pcr_digest: Option, + // Whether to use a policy. If this is specified without pubkey path or policy path, they get set to defaults + pub use_policy: Option, + // Public key (in JSON format) for a wildcard policy that's possibly OR'd with the PCR one + pub policy_pubkey_path: Option, + pub policy_ref: Option, + pub policy_path: Option, +} + +impl TryFrom<&TPM2Config> for TPMPolicyStep { + type Error = Error; + + fn try_from(cfg: &TPM2Config) -> Result { + if cfg.pcr_ids.is_some() && cfg.policy_pubkey_path.is_some() { + Ok(TPMPolicyStep::Or([ + Box::new(TPMPolicyStep::PCRs( + cfg.get_pcr_hash_alg(), + cfg.get_pcr_ids().unwrap(), + Box::new(TPMPolicyStep::NoStep), + )), + Box::new(get_authorized_policy_step( + cfg.policy_pubkey_path.as_ref().unwrap(), + &None, + &cfg.policy_ref, + )?), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + ])) + } else if cfg.pcr_ids.is_some() { + Ok(TPMPolicyStep::PCRs( + cfg.get_pcr_hash_alg(), + cfg.get_pcr_ids().unwrap(), + Box::new(TPMPolicyStep::NoStep), + )) + } else if cfg.policy_pubkey_path.is_some() { + get_authorized_policy_step( + cfg.policy_pubkey_path.as_ref().unwrap(), + &None, + &cfg.policy_ref, + ) + } else { + Ok(TPMPolicyStep::NoStep) + } + } +} + +const DEFAULT_POLICY_PATH: &str = "/boot/clevis_policy.json"; +const DEFAULT_PUBKEY_PATH: &str = "/boot/clevis_pubkey.json"; +const DEFAULT_POLICY_REF: &str = ""; + +impl TPM2Config { + pub(super) fn get_pcr_hash_alg( + &self, + ) -> tss_esapi::interface_types::algorithm::HashingAlgorithm { + super::utils::get_hash_alg_from_name(self.pcr_bank.as_ref()) + } + + pub(super) fn get_name_hash_alg( + &self, + ) -> tss_esapi::interface_types::algorithm::HashingAlgorithm { + super::utils::get_hash_alg_from_name(self.hash.as_ref()) + } + + pub(super) fn get_pcr_ids(&self) -> Option> { + match &self.pcr_ids { + None => None, + Some(serde_json::Value::Array(vals)) => { + Some(vals.iter().map(|x| x.as_u64().unwrap()).collect()) + } + _ => panic!("Unexpected type found for pcr_ids"), + } + } + + pub(super) fn get_pcr_ids_str(&self) -> Option { + match &self.pcr_ids { + None => None, + Some(serde_json::Value::Array(vals)) => Some( + vals.iter() + .map(|x| x.as_u64().unwrap().to_string()) + .collect::>() + .join(","), + ), + _ => panic!("Unexpected type found for pcr_ids"), + } + } + + pub fn normalize(mut self) -> Result { + self.normalize_pcr_ids()?; + if self.pcr_ids.is_some() && self.pcr_bank.is_none() { + self.pcr_bank = Some("sha256".to_string()); + } + // Make use of the defaults if not specified + if self.use_policy.is_some() && self.use_policy.unwrap() { + if self.policy_path.is_none() { + self.policy_path = Some(DEFAULT_POLICY_PATH.to_string()); + } + if self.policy_pubkey_path.is_none() { + self.policy_pubkey_path = Some(DEFAULT_PUBKEY_PATH.to_string()); + } + if self.policy_ref.is_none() { + self.policy_ref = Some(DEFAULT_POLICY_REF.to_string()); + } + } else if self.policy_pubkey_path.is_some() + || self.policy_path.is_some() + || self.policy_ref.is_some() + { + eprintln!("To use a policy, please specifiy use_policy: true. Not specifying this will be a fatal error in a next release"); + } + if (self.policy_pubkey_path.is_some() + || self.policy_path.is_some() + || self.policy_ref.is_some()) + && (self.policy_pubkey_path.is_none() + || self.policy_path.is_none() + || self.policy_ref.is_none()) + { + bail!("Not all of policy pubkey, path and ref are specified",); + } + Ok(self) + } + + fn normalize_pcr_ids(&mut self) -> Result<()> { + // Normalize from array with one string to just string + if let Some(serde_json::Value::Array(vals)) = &self.pcr_ids { + if vals.len() == 1 { + if let serde_json::Value::String(val) = &vals[0] { + self.pcr_ids = Some(serde_json::Value::String(val.to_string())); + } + } + } + // Normalize pcr_ids from comma-separated string to array + if let Some(serde_json::Value::String(val)) = &self.pcr_ids { + // Was a string, do a split + let newval: Vec = val + .split(',') + .map(|x| serde_json::Value::String(x.trim().to_string())) + .collect(); + self.pcr_ids = Some(serde_json::Value::Array(newval)); + } + // Normalize pcr_ids from array of Strings to array of Numbers + if let Some(serde_json::Value::Array(vals)) = &self.pcr_ids { + let newvals: Result, _> = vals + .iter() + .map(|x| match x { + serde_json::Value::String(val) => { + match val.trim().parse::() { + Ok(res) => { + let new = serde_json::Value::Number(res); + if !new.is_u64() { + bail!("Non-positive string int"); + } + Ok(new) + } + Err(_) => Err(eyre!("Unparseable string int")), + } + } + serde_json::Value::Number(n) => { + let new = serde_json::Value::Number(n.clone()); + if !new.is_u64() { + return Err(eyre!("Non-positive int")); + } + Ok(new) + } + _ => Err(eyre!("Invalid value in pcr_ids")), + }) + .collect(); + self.pcr_ids = Some(serde_json::Value::Array(newvals?)); + } + + match &self.pcr_ids { + None => Ok(()), + // The normalization above would've caught any non-ints + Some(serde_json::Value::Array(_)) => Ok(()), + _ => Err(eyre!("Invalid type")), + } + } +} + +#[cfg(target_pointer_width = "64")] +type Sizedu = u64; +#[cfg(target_pointer_width = "32")] +type Sizedu = u32; + +pub(super) fn get_key_public( + key_type: &str, + name_alg: HashingAlgorithm, +) -> color_eyre::Result { + let object_attributes = ObjectAttributesBuilder::new() + .with_fixed_tpm(true) + .with_fixed_parent(true) + .with_sensitive_data_origin(true) + .with_user_with_auth(true) + .with_decrypt(true) + .with_sign_encrypt(false) + .with_restricted(true) + .build()?; + + let builder = tss_esapi::structures::PublicBuilder::new() + .with_object_attributes(object_attributes) + .with_name_hashing_algorithm(name_alg); + + match key_type { + "ecc" => builder + .with_public_algorithm(PublicAlgorithm::Ecc) + .with_ecc_parameters( + tss_esapi::structures::PublicEccParametersBuilder::new_restricted_decryption_key( + SymmetricDefinitionObject::AES_128_CFB, + EccCurve::NistP256, + ) + .build()?, + ) + .with_ecc_unique_identifier(Default::default()), + "rsa" => builder + .with_public_algorithm(PublicAlgorithm::Rsa) + .with_rsa_parameters( + tss_esapi::structures::PublicRsaParametersBuilder::new_restricted_decryption_key( + SymmetricDefinitionObject::AES_128_CFB, + tss_esapi::interface_types::key_bits::RsaKeyBits::Rsa2048, + tss_esapi::structures::RsaExponent::ZERO_EXPONENT, + ) + .build()?, + ) + .with_rsa_unique_identifier(Default::default()), + _ => return Err(eyre!("Unsupported key type used")), + } + .build() + .context("Error building public key") +} + +pub(super) fn create_tpm2b_public_sealed_object( + policy: Option, +) -> Result { + let mut object_attributes = ObjectAttributesBuilder::new() + .with_fixed_tpm(true) + .with_fixed_parent(true) + .with_no_da(true) + .with_admin_with_policy(true); + + if policy.is_none() { + object_attributes = object_attributes.with_user_with_auth(true); + } + let policy = match policy { + Some(p) => p, + None => Digest::try_from(vec![])?, + }; + + let mut params: tss_esapi::tss2_esys::TPMU_PUBLIC_PARMS = Default::default(); + params.keyedHashDetail.scheme.scheme = tss_constants::TPM2_ALG_NULL; + + Ok(tss_esapi::tss2_esys::TPM2B_PUBLIC { + size: std::mem::size_of::() as u16, + publicArea: tss_esapi::tss2_esys::TPMT_PUBLIC { + type_: tss_constants::TPM2_ALG_KEYEDHASH, + nameAlg: tss_constants::TPM2_ALG_SHA256, + objectAttributes: object_attributes.build()?.0, + authPolicy: tss_esapi::tss2_esys::TPM2B_DIGEST::from(policy), + parameters: params, + unique: Default::default(), + }, + }) +} + +pub(super) fn get_tpm2b_public(val: tss_esapi::tss2_esys::TPM2B_PUBLIC) -> Result> { + let mut offset = 0 as Sizedu; + let mut resp = Vec::with_capacity((val.size + 4) as usize); + + unsafe { + let res = tss_esapi::tss2_esys::Tss2_MU_TPM2B_PUBLIC_Marshal( + &val, + resp.as_mut_ptr(), + resp.capacity() as Sizedu, + &mut offset, + ); + if res != 0 { + bail!("Marshalling tpm2b_public failed"); + } + resp.set_len(offset as usize); + } + + Ok(resp) +} + +pub(super) fn get_tpm2b_private(val: tss_esapi::tss2_esys::TPM2B_PRIVATE) -> Result> { + let mut offset = 0 as Sizedu; + let mut resp = Vec::with_capacity((val.size + 4) as usize); + + unsafe { + let res = tss_esapi::tss2_esys::Tss2_MU_TPM2B_PRIVATE_Marshal( + &val, + resp.as_mut_ptr(), + resp.capacity() as Sizedu, + &mut offset, + ); + if res != 0 { + bail!("Marshalling tpm2b_private failed"); + } + resp.set_len(offset as usize); + } + + Ok(resp) +} + +pub(super) fn build_tpm2b_private(val: &[u8]) -> Result { + let mut resp = tss_esapi::tss2_esys::TPM2B_PRIVATE::default(); + let mut offset = 0 as Sizedu; + + unsafe { + let res = tss_esapi::tss2_esys::Tss2_MU_TPM2B_PRIVATE_Unmarshal( + val[..].as_ptr(), + val.len() as Sizedu, + &mut offset, + &mut resp, + ); + if res != 0 { + bail!("Unmarshalling tpm2b_private failed"); + } + } + + Ok(resp) +} + +pub(super) fn build_tpm2b_public(val: &[u8]) -> Result { + let mut resp = tss_esapi::tss2_esys::TPM2B_PUBLIC::default(); + let mut offset = 0 as Sizedu; + + unsafe { + let res = tss_esapi::tss2_esys::Tss2_MU_TPM2B_PUBLIC_Unmarshal( + val[..].as_ptr(), + val.len() as Sizedu, + &mut offset, + &mut resp, + ); + if res != 0 { + bail!("Unmarshalling tpm2b_public failed"); + } + } + + Ok(resp) +} diff --git a/src/tpm/utils.rs b/src/tpm/utils.rs new file mode 100644 index 0000000..dbeeaa2 --- /dev/null +++ b/src/tpm/utils.rs @@ -0,0 +1,110 @@ +use std::env; +use std::fs; +use std::str::FromStr; + +use base64::engine::general_purpose::URL_SAFE_NO_PAD; +use base64::Engine; +use color_eyre::eyre::WrapErr; +use color_eyre::Result; +use serde::Deserialize; +use tpm2_policy::{PublicKey, SignedPolicyList, TPMPolicyStep}; +use tss_esapi::{ + handles::KeyHandle, + interface_types::{algorithm::HashingAlgorithm, resource_handles::Hierarchy}, + structures::Public, + Context, Tcti, +}; + +pub(crate) fn get_authorized_policy_step( + policy_pubkey_path: &str, + policy_path: &Option, + policy_ref: &Option, +) -> Result { + let policy_ref = match policy_ref { + Some(policy_ref) => policy_ref.as_bytes().to_vec(), + None => vec![], + }; + + let signkey = { + let contents = + fs::read_to_string(policy_pubkey_path).context("Error reading policy signkey")?; + serde_json::from_str::(&contents) + .context("Error deserializing signing public key")? + }; + + let policies = match policy_path { + None => None, + Some(policy_path) => { + let contents = fs::read_to_string(policy_path).context("Error reading policy")?; + Some( + serde_json::from_str::(&contents) + .context("Error deserializing policy")?, + ) + } + }; + + Ok(TPMPolicyStep::Authorized { + signkey, + policy_ref, + policies, + next: Box::new(TPMPolicyStep::NoStep), + }) +} + +pub(crate) fn get_hash_alg_from_name(name: Option<&String>) -> HashingAlgorithm { + match name { + None => HashingAlgorithm::Sha256, + Some(val) => match val.to_lowercase().as_str() { + "sha1" => HashingAlgorithm::Sha1, + "sha256" => HashingAlgorithm::Sha256, + "sha384" => HashingAlgorithm::Sha384, + "sha512" => HashingAlgorithm::Sha512, + _ => panic!("Unsupported hash algo: {:?}", name), + }, + } +} + +pub(crate) fn serialize_as_base64_url_no_pad( + bytes: &[u8], + serializer: S, +) -> Result +where + S: serde::Serializer, +{ + serializer.serialize_str(&URL_SAFE_NO_PAD.encode(bytes)) +} + +pub(crate) fn deserialize_as_base64_url_no_pad<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + String::deserialize(deserializer).and_then(|string| { + URL_SAFE_NO_PAD + .decode(string) + .map_err(serde::de::Error::custom) + }) +} + +pub(crate) fn get_tpm2_ctx() -> Result { + let tcti_path = match env::var("TCTI") { + Ok(val) => val, + Err(_) => { + if std::path::Path::new("/dev/tpmrm0").exists() { + "device:/dev/tpmrm0".to_string() + } else { + "device:/dev/tpm0".to_string() + } + } + }; + + let tcti = Tcti::from_str(&tcti_path).context("Error parsing TCTI specification")?; + Context::new(tcti).context("Error initializing TPM2 context") +} + +pub(crate) fn get_tpm2_primary_key(ctx: &mut Context, pub_template: Public) -> Result { + ctx.execute_with_nullauth_session(|ctx| { + ctx.create_primary(Hierarchy::Owner, pub_template, None, None, None, None) + .map(|r| r.key_handle) + }) + .map_err(|e| e.into()) +} From b36aa43b3a27a97cedf44b69086d3bf5cdfa980b Mon Sep 17 00:00:00 2001 From: Vivian Roest Date: Tue, 19 Dec 2023 09:00:07 +0100 Subject: [PATCH 06/20] tpm2 working --- Cargo.lock | 140 ++++++++++------ Cargo.toml | 15 +- flake.nix | 67 +++++--- result | 1 + src/main.rs | 36 ++-- src/tpm/mod.rs | 216 ++++++++++++++++++++++++ src/tpm/tpm_objects.rs | 366 +++++++++++++++++++++++++++++++++++++++++ src/tpm/utils.rs | 110 +++++++++++++ 8 files changed, 862 insertions(+), 89 deletions(-) create mode 120000 result create mode 100644 src/tpm/mod.rs create mode 100644 src/tpm/tpm_objects.rs create mode 100644 src/tpm/utils.rs diff --git a/Cargo.lock b/Cargo.lock index dd9bd14..62d85a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,17 +80,6 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -118,12 +107,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.5" @@ -136,7 +119,7 @@ version = "0.66.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" dependencies = [ - "bitflags", + "bitflags 2.4.1", "cexpr", "clang-sys", "lazy_static", @@ -159,6 +142,12 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.4.1" @@ -240,20 +229,6 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" -[[package]] -name = "clevis-pin-tpm2" -version = "0.5.3" -dependencies = [ - "anyhow", - "atty", - "base64 0.12.3", - "josekit", - "serde", - "serde_json", - "tpm2-policy", - "tss-esapi", -] - [[package]] name = "color-eyre" version = "0.6.2" @@ -305,6 +280,27 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "either" version = "1.9.0" @@ -382,6 +378,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.28.1" @@ -398,12 +405,16 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" name = "gnome-autounlock-keyring" version = "0.1.0" dependencies = [ + "base64 0.21.5", "clap", - "clevis-pin-tpm2", "color-eyre", + "dirs", + "josekit", "rpassword", "serde", "serde_json", + "tpm2-policy", + "tss-esapi", ] [[package]] @@ -418,15 +429,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "home" version = "0.5.9" @@ -466,16 +468,17 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "josekit" -version = "0.7.4" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e84ea7acc05b40e2fe6fa02a54b3731323c77e6015c36749f0b10c4dbbc32f" +checksum = "5754487a088f527b1407df470db8e654e4064dccbbe1fe850e0773721e9962b7" dependencies = [ "anyhow", - "base64 0.13.1", + "base64 0.21.5", "flate2", "once_cell", "openssl", "regex", + "serde", "serde_json", "thiserror", "time", @@ -509,6 +512,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] + [[package]] name = "linux-raw-sys" version = "0.4.12" @@ -613,7 +627,7 @@ version = "0.10.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" dependencies = [ - "bitflags", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -645,6 +659,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "owo-colors" version = "3.5.0" @@ -749,6 +769,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.2" @@ -826,7 +866,7 @@ version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ - "bitflags", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", @@ -1105,6 +1145,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "which" version = "4.4.2" diff --git a/Cargo.toml b/Cargo.toml index 9020bf0..97be2f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,15 @@ edition = "2021" [dependencies] color-eyre = "0.6.2" -serde_json = "1.0.108" -serde = { version = "1.0.193", features = ["derive"] } -clevis-pin-tpm2 = { path = "../clevis-pin-tpm2" } +serde_json = "1" +serde = { version = "1", features = ["derive"] } clap = { version = "4", features = ["derive"] } -rpassword = "7.3.1" \ No newline at end of file +rpassword = "7.3.1" +tss-esapi = { version = "7.2", features = ["generate-bindings"] } +josekit = "0.8.4" +base64 = { version = "0.21.5", features = [] } +tpm2-policy = "0.6.0" +dirs = "5" + +[profile.release] +lto = "thin" diff --git a/flake.nix b/flake.nix index ec6ef15..fdb20f9 100644 --- a/flake.nix +++ b/flake.nix @@ -7,40 +7,55 @@ outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let + cargoToml = (builtins.fromTOML (builtins.readFile ./Cargo.toml)); pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) stdenv lib; - in { - devShell = pkgs.mkShell { - shellHook = '' - export BINDGEN_EXTRA_CLANG_ARGS="$(< ${stdenv.cc}/nix-support/libc-crt1-cflags) \ - $(< ${stdenv.cc}/nix-support/libc-cflags) \ - $(< ${stdenv.cc}/nix-support/cc-cflags) \ - $(< ${stdenv.cc}/nix-support/libcxx-cxxflags) \ - ${ - lib.optionalString stdenv.cc.isClang - "-idirafter ${stdenv.cc.cc}/lib/clang/${ - lib.getVersion stdenv.cc.cc - }/include" - } \ - ${ - lib.optionalString stdenv.cc.isGNU - "-isystem ${stdenv.cc.cc}/include/c++/${ - lib.getVersion stdenv.cc.cc - } -isystem ${stdenv.cc.cc}/include/c++/${ - lib.getVersion stdenv.cc.cc - }/${stdenv.hostPlatform.config} -idirafter ${stdenv.cc.cc}/lib/gcc/${stdenv.hostPlatform.config}/${ - lib.getVersion stdenv.cc.cc - }/include" - } \ - " - ''; + in rec { + packages.default = pkgs.rustPlatform.buildRustPackage { + pname = cargoToml.package.name; + version = cargoToml.package.version; + src = self; + cargoLock.lockFile = ./Cargo.lock; + + doCheck = false; + nativeBuildInputs = with pkgs; [ llvmPackages.libclang llvmPackages.libcxxClang clang + pkg-config ]; LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; - buildInputs = with pkgs; [ libclang pkg-config openssl tpm2-tss ]; + buildInputs = with pkgs; [ openssl tpm2-tss ]; + + preBuild = '' + export BINDGEN_EXTRA_CLANG_ARGS="$(< ${stdenv.cc}/nix-support/libc-crt1-cflags) \ + $(< ${stdenv.cc}/nix-support/libc-cflags) \ + $(< ${stdenv.cc}/nix-support/cc-cflags) \ + $(< ${stdenv.cc}/nix-support/libcxx-cxxflags) \ + ${ + lib.optionalString stdenv.cc.isClang + "-idirafter ${stdenv.cc.cc}/lib/clang/${ + lib.getVersion stdenv.cc.cc + }/include" + } \ + ${ + lib.optionalString stdenv.cc.isGNU + "-isystem ${stdenv.cc.cc}/include/c++/${ + lib.getVersion stdenv.cc.cc + } -isystem ${stdenv.cc.cc}/include/c++/${ + lib.getVersion stdenv.cc.cc + }/${stdenv.hostPlatform.config} -idirafter ${stdenv.cc.cc}/lib/gcc/${stdenv.hostPlatform.config}/${ + lib.getVersion stdenv.cc.cc + }/include" + } \ + " + ''; + }; + + devShell = pkgs.mkShell { + shellHook = "${packages.default.preBuild}"; + inherit (packages.default) nativeBuildInputs buildInputs LIBCLANG_PATH; }; }); } diff --git a/result b/result new file mode 120000 index 0000000..b5c3c34 --- /dev/null +++ b/result @@ -0,0 +1 @@ +/nix/store/bwvjqn0n9xljxk646l0rqj6m0ym221m7-gnome-autounlock-keyring-0.1.0 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index a332885..54bcc66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,14 @@ +mod tpm; + use clap::{Parser, Subcommand}; -use clevis_pin_tpm2::tpm_objects::TPM2Config; use color_eyre::eyre::{bail, eyre, WrapErr}; use std::env; use std::fs::{read_to_string, File}; -use std::io::{stdin, Read, Write}; +use std::io::{Read, Write}; use std::os::unix::net::UnixStream; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::process::exit; +use tpm::tpm_objects::TPM2Config; fn get_control_socket() -> Option { let gnome_var = env::var("GNOME_KEYRING_CONTROL") @@ -22,6 +24,7 @@ fn get_control_socket() -> Option { gnome_var.or(xdg_var) } +#[derive(Debug, Clone, Copy)] enum ControlOp { Initialize = 0, Unlock = 1, @@ -108,13 +111,19 @@ fn unlock_keyring(password: &[u8]) -> color_eyre::Result { #[derive(Parser)] struct Cli { + /// Defaults to CONFIG_DIR/gnome-keyring.tpm2 + #[arg(short, long)] + token_path: Option, + #[command(subcommand)] command: Commands, } #[derive(Subcommand)] enum Commands { + /// Unlock gnome keyring using encrypted password stored in tpm Unlock, + /// Enroll a password into the tpm to use when unlocking Enroll, } @@ -122,13 +131,17 @@ fn main() -> color_eyre::Result<()> { color_eyre::install().unwrap(); let cli = Cli::parse(); + let token_path = cli + .token_path + .or(dirs::config_dir().map(|el| el.join("gnome-keyring.tpm2"))) + .ok_or_else(|| eyre!("Token path not found"))?; + match cli.command { Commands::Unlock => { - let file = PathBuf::from("/home/vivian/.config/gnome_password.token"); - if file.exists() { - let token = read_to_string(file)?; - let password = clevis_pin_tpm2::perform_decrypt(token.as_bytes()) - .map_err(|err| eyre!("{err:?}"))?; + if token_path.exists() { + let token = read_to_string(token_path)?; + let password = + tpm::perform_decrypt(token.as_bytes()).map_err(|err| eyre!("{err:?}"))?; let res = unlock_keyring(password.as_slice())?; if res != ControlResult::Ok { eprintln!("Failed to unlock keyring: {res:?}"); @@ -147,10 +160,9 @@ fn main() -> color_eyre::Result<()> { exit(3); } - let token = - clevis_pin_tpm2::perform_encrypt(TPM2Config::default(), password.as_bytes()) - .map_err(|err| eyre!("{err:?}"))?; - let mut file = File::create("/home/vivian/.config/gnome_password.token")?; + let token = tpm::perform_encrypt(TPM2Config::default(), password.as_bytes()) + .map_err(|err| eyre!("{err:?}"))?; + let mut file = File::create(token_path)?; file.write_all(token.as_bytes())?; println!("Password enrolled successfully") } diff --git a/src/tpm/mod.rs b/src/tpm/mod.rs new file mode 100644 index 0000000..6210eee --- /dev/null +++ b/src/tpm/mod.rs @@ -0,0 +1,216 @@ +// Copyright 2020 Patrick Uiterwijk +// +// Licensed under the MIT license + +use std::convert::{TryFrom, TryInto}; + +use color_eyre::{ + eyre::{bail, Context, ContextCompat, Error}, + Result, +}; +use josekit::jwe::{alg::direct::DirectJweAlgorithm::Dir, enc::A256GCM}; +use serde::{Deserialize, Serialize}; +use tpm2_policy::TPMPolicyStep; +use tpm_objects::TPM2Config; +use tss_esapi::structures::SensitiveData; + +pub mod tpm_objects; +pub mod utils; + +pub fn perform_encrypt(cfg: TPM2Config, input: &[u8]) -> Result { + let key_type = match &cfg.key { + None => "ecc", + Some(key_type) => key_type, + }; + let key_public = tpm_objects::get_key_public(key_type, cfg.get_name_hash_alg())?; + + let mut ctx = utils::get_tpm2_ctx()?; + let key_handle = utils::get_tpm2_primary_key(&mut ctx, key_public)?; + + let policy_runner: TPMPolicyStep = TPMPolicyStep::try_from(&cfg)?; + + let pin_type = match policy_runner { + TPMPolicyStep::NoStep => "tpm2", + TPMPolicyStep::PCRs(_, _, _) => "tpm2", + _ => "tpm2plus", + }; + + let (_, policy_digest) = policy_runner.send_policy(&mut ctx, true)?; + + let mut jwk = josekit::jwk::Jwk::generate_oct_key(32).context("Error generating random JWK")?; + jwk.set_key_operations(vec!["encrypt", "decrypt"]); + let jwk_str = serde_json::to_string(&jwk.as_ref())?; + + let public = tpm_objects::create_tpm2b_public_sealed_object(policy_digest)?.try_into()?; + let jwk_str = SensitiveData::try_from(jwk_str.as_bytes().to_vec())?; + let jwk_result = ctx.execute_with_nullauth_session(|ctx| { + ctx.create(key_handle, public, None, Some(jwk_str), None, None) + })?; + + let jwk_priv = tpm_objects::get_tpm2b_private(jwk_result.out_private.into())?; + + let jwk_pub = tpm_objects::get_tpm2b_public(jwk_result.out_public.try_into()?)?; + + let private_hdr = ClevisInner { + pin: pin_type.to_string(), + tpm2: Tpm2Inner { + hash: cfg.hash.as_ref().unwrap_or(&"sha256".to_string()).clone(), + key: key_type.to_string(), + jwk_pub, + jwk_priv, + pcr_bank: cfg.pcr_bank.clone(), + pcr_ids: cfg.get_pcr_ids_str(), + policy_pubkey_path: cfg.policy_pubkey_path, + policy_ref: cfg.policy_ref, + policy_path: cfg.policy_path, + }, + }; + + let mut hdr = josekit::jwe::JweHeader::new(); + hdr.set_algorithm(Dir.name()); + hdr.set_content_encryption(A256GCM.name()); + hdr.set_claim( + "clevis", + Some(serde_json::value::to_value(private_hdr).context("Error serializing private header")?), + ) + .context("Error adding clevis claim")?; + + let encrypter = Dir + .encrypter_from_jwk(&jwk) + .context("Error creating direct encrypter")?; + let jwe_token = josekit::jwe::serialize_compact(input, &hdr, &encrypter) + .context("Error serializing JWE token")?; + + Ok(jwe_token) +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +struct Tpm2Inner { + hash: String, + #[serde( + deserialize_with = "utils::deserialize_as_base64_url_no_pad", + serialize_with = "utils::serialize_as_base64_url_no_pad" + )] + jwk_priv: Vec, + #[serde( + deserialize_with = "utils::deserialize_as_base64_url_no_pad", + serialize_with = "utils::serialize_as_base64_url_no_pad" + )] + jwk_pub: Vec, + key: String, + + // PCR Binding may be specified, may not + #[serde(skip_serializing_if = "Option::is_none")] + pcr_bank: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pcr_ids: Option, + + // Public key (in PEM format) for a wildcard policy that's OR'd with the PCR one + #[serde(skip_serializing_if = "Option::is_none")] + policy_pubkey_path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + policy_ref: Option, + #[serde(skip_serializing_if = "Option::is_none")] + policy_path: Option, +} + +impl Tpm2Inner { + fn get_pcr_ids(&self) -> Option> { + Some( + self.pcr_ids + .as_ref()? + .split(',') + .map(|x| x.parse::().unwrap()) + .collect(), + ) + } +} + +impl TryFrom<&Tpm2Inner> for TPMPolicyStep { + type Error = Error; + + fn try_from(cfg: &Tpm2Inner) -> Result { + if cfg.pcr_ids.is_some() && cfg.policy_pubkey_path.is_some() { + Ok(TPMPolicyStep::Or([ + Box::new(TPMPolicyStep::PCRs( + utils::get_hash_alg_from_name(cfg.pcr_bank.as_ref()), + cfg.get_pcr_ids().unwrap(), + Box::new(TPMPolicyStep::NoStep), + )), + Box::new(utils::get_authorized_policy_step( + cfg.policy_pubkey_path.as_ref().unwrap(), + &cfg.policy_path, + &cfg.policy_ref, + )?), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + ])) + } else if cfg.pcr_ids.is_some() { + Ok(TPMPolicyStep::PCRs( + utils::get_hash_alg_from_name(cfg.pcr_bank.as_ref()), + cfg.get_pcr_ids().unwrap(), + Box::new(TPMPolicyStep::NoStep), + )) + } else if cfg.policy_pubkey_path.is_some() { + utils::get_authorized_policy_step( + cfg.policy_pubkey_path.as_ref().unwrap(), + &cfg.policy_path, + &cfg.policy_ref, + ) + } else { + Ok(TPMPolicyStep::NoStep) + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +struct ClevisInner { + pin: String, + tpm2: Tpm2Inner, +} + +pub fn perform_decrypt(input: &[u8]) -> Result> { + let input = String::from_utf8(input.to_vec()).context("Error reading input")?; + let hdr = josekit::jwt::decode_header(&input).context("Error decoding header")?; + let hdr_clevis = hdr.claim("clevis").context("Error getting clevis claim")?; + let hdr_clevis: ClevisInner = + serde_json::from_value(hdr_clevis.clone()).context("Error deserializing clevis header")?; + + if hdr_clevis.pin != "tpm2" && hdr_clevis.pin != "tpm2plus" { + bail!("JWE pin mismatch"); + } + + let jwkpub = tpm_objects::build_tpm2b_public(&hdr_clevis.tpm2.jwk_pub)?.try_into()?; + let jwkpriv = tpm_objects::build_tpm2b_private(&hdr_clevis.tpm2.jwk_priv)?; + + let policy = TPMPolicyStep::try_from(&hdr_clevis.tpm2)?; + + let name_alg = utils::get_hash_alg_from_name(Some(&hdr_clevis.tpm2.hash)); + let key_public = tpm_objects::get_key_public(hdr_clevis.tpm2.key.as_str(), name_alg)?; + + let mut ctx = utils::get_tpm2_ctx()?; + let key_handle = utils::get_tpm2_primary_key(&mut ctx, key_public)?; + + let key = + ctx.execute_with_nullauth_session(|ctx| ctx.load(key_handle, jwkpriv.try_into()?, jwkpub))?; + + let (policy_session, _) = policy.send_policy(&mut ctx, false)?; + + let unsealed = ctx.execute_with_session(policy_session, |ctx| ctx.unseal(key.into()))?; + let unsealed = &unsealed.value(); + let mut jwk = josekit::jwk::Jwk::from_bytes(unsealed).context("Error unmarshaling JWK")?; + jwk.set_parameter("alg", None) + .context("Error removing the alg parameter")?; + let decrypter = Dir + .decrypter_from_jwk(&jwk) + .context("Error creating decrypter")?; + + let (payload, _) = + josekit::jwe::deserialize_compact(&input, &decrypter).context("Error decrypting JWE")?; + + Ok(payload) +} diff --git a/src/tpm/tpm_objects.rs b/src/tpm/tpm_objects.rs new file mode 100644 index 0000000..5d93c75 --- /dev/null +++ b/src/tpm/tpm_objects.rs @@ -0,0 +1,366 @@ +use color_eyre::eyre::{bail, eyre, Context, Error}; +use std::convert::TryFrom; + +use super::utils::get_authorized_policy_step; +use color_eyre::Result; +use serde::{Deserialize, Serialize}; +use tpm2_policy::TPMPolicyStep; +use tss_esapi::{ + attributes::object::ObjectAttributesBuilder, + constants::tss as tss_constants, + interface_types::{ + algorithm::{HashingAlgorithm, PublicAlgorithm}, + ecc::EccCurve, + }, + structures::{Digest, Public, SymmetricDefinitionObject}, +}; + +#[derive(Serialize, Deserialize, Default, std::fmt::Debug)] +pub struct TPM2Config { + pub hash: Option, + pub key: Option, + pub pcr_bank: Option, + // PCR IDs can be passed in as comma-separated string or json array + pub pcr_ids: Option, + pub pcr_digest: Option, + // Whether to use a policy. If this is specified without pubkey path or policy path, they get set to defaults + pub use_policy: Option, + // Public key (in JSON format) for a wildcard policy that's possibly OR'd with the PCR one + pub policy_pubkey_path: Option, + pub policy_ref: Option, + pub policy_path: Option, +} + +impl TryFrom<&TPM2Config> for TPMPolicyStep { + type Error = Error; + + fn try_from(cfg: &TPM2Config) -> Result { + if cfg.pcr_ids.is_some() && cfg.policy_pubkey_path.is_some() { + Ok(TPMPolicyStep::Or([ + Box::new(TPMPolicyStep::PCRs( + cfg.get_pcr_hash_alg(), + cfg.get_pcr_ids().unwrap(), + Box::new(TPMPolicyStep::NoStep), + )), + Box::new(get_authorized_policy_step( + cfg.policy_pubkey_path.as_ref().unwrap(), + &None, + &cfg.policy_ref, + )?), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + Box::new(TPMPolicyStep::NoStep), + ])) + } else if cfg.pcr_ids.is_some() { + Ok(TPMPolicyStep::PCRs( + cfg.get_pcr_hash_alg(), + cfg.get_pcr_ids().unwrap(), + Box::new(TPMPolicyStep::NoStep), + )) + } else if cfg.policy_pubkey_path.is_some() { + get_authorized_policy_step( + cfg.policy_pubkey_path.as_ref().unwrap(), + &None, + &cfg.policy_ref, + ) + } else { + Ok(TPMPolicyStep::NoStep) + } + } +} + +const DEFAULT_POLICY_PATH: &str = "/boot/clevis_policy.json"; +const DEFAULT_PUBKEY_PATH: &str = "/boot/clevis_pubkey.json"; +const DEFAULT_POLICY_REF: &str = ""; + +impl TPM2Config { + pub(super) fn get_pcr_hash_alg( + &self, + ) -> tss_esapi::interface_types::algorithm::HashingAlgorithm { + super::utils::get_hash_alg_from_name(self.pcr_bank.as_ref()) + } + + pub(super) fn get_name_hash_alg( + &self, + ) -> tss_esapi::interface_types::algorithm::HashingAlgorithm { + super::utils::get_hash_alg_from_name(self.hash.as_ref()) + } + + pub(super) fn get_pcr_ids(&self) -> Option> { + match &self.pcr_ids { + None => None, + Some(serde_json::Value::Array(vals)) => { + Some(vals.iter().map(|x| x.as_u64().unwrap()).collect()) + } + _ => panic!("Unexpected type found for pcr_ids"), + } + } + + pub(super) fn get_pcr_ids_str(&self) -> Option { + match &self.pcr_ids { + None => None, + Some(serde_json::Value::Array(vals)) => Some( + vals.iter() + .map(|x| x.as_u64().unwrap().to_string()) + .collect::>() + .join(","), + ), + _ => panic!("Unexpected type found for pcr_ids"), + } + } + + pub fn normalize(mut self) -> Result { + self.normalize_pcr_ids()?; + if self.pcr_ids.is_some() && self.pcr_bank.is_none() { + self.pcr_bank = Some("sha256".to_string()); + } + // Make use of the defaults if not specified + if self.use_policy.is_some() && self.use_policy.unwrap() { + if self.policy_path.is_none() { + self.policy_path = Some(DEFAULT_POLICY_PATH.to_string()); + } + if self.policy_pubkey_path.is_none() { + self.policy_pubkey_path = Some(DEFAULT_PUBKEY_PATH.to_string()); + } + if self.policy_ref.is_none() { + self.policy_ref = Some(DEFAULT_POLICY_REF.to_string()); + } + } else if self.policy_pubkey_path.is_some() + || self.policy_path.is_some() + || self.policy_ref.is_some() + { + eprintln!("To use a policy, please specifiy use_policy: true. Not specifying this will be a fatal error in a next release"); + } + if (self.policy_pubkey_path.is_some() + || self.policy_path.is_some() + || self.policy_ref.is_some()) + && (self.policy_pubkey_path.is_none() + || self.policy_path.is_none() + || self.policy_ref.is_none()) + { + bail!("Not all of policy pubkey, path and ref are specified",); + } + Ok(self) + } + + fn normalize_pcr_ids(&mut self) -> Result<()> { + // Normalize from array with one string to just string + if let Some(serde_json::Value::Array(vals)) = &self.pcr_ids { + if vals.len() == 1 { + if let serde_json::Value::String(val) = &vals[0] { + self.pcr_ids = Some(serde_json::Value::String(val.to_string())); + } + } + } + // Normalize pcr_ids from comma-separated string to array + if let Some(serde_json::Value::String(val)) = &self.pcr_ids { + // Was a string, do a split + let newval: Vec = val + .split(',') + .map(|x| serde_json::Value::String(x.trim().to_string())) + .collect(); + self.pcr_ids = Some(serde_json::Value::Array(newval)); + } + // Normalize pcr_ids from array of Strings to array of Numbers + if let Some(serde_json::Value::Array(vals)) = &self.pcr_ids { + let newvals: Result, _> = vals + .iter() + .map(|x| match x { + serde_json::Value::String(val) => { + match val.trim().parse::() { + Ok(res) => { + let new = serde_json::Value::Number(res); + if !new.is_u64() { + bail!("Non-positive string int"); + } + Ok(new) + } + Err(_) => Err(eyre!("Unparseable string int")), + } + } + serde_json::Value::Number(n) => { + let new = serde_json::Value::Number(n.clone()); + if !new.is_u64() { + return Err(eyre!("Non-positive int")); + } + Ok(new) + } + _ => Err(eyre!("Invalid value in pcr_ids")), + }) + .collect(); + self.pcr_ids = Some(serde_json::Value::Array(newvals?)); + } + + match &self.pcr_ids { + None => Ok(()), + // The normalization above would've caught any non-ints + Some(serde_json::Value::Array(_)) => Ok(()), + _ => Err(eyre!("Invalid type")), + } + } +} + +#[cfg(target_pointer_width = "64")] +type Sizedu = u64; +#[cfg(target_pointer_width = "32")] +type Sizedu = u32; + +pub(super) fn get_key_public( + key_type: &str, + name_alg: HashingAlgorithm, +) -> color_eyre::Result { + let object_attributes = ObjectAttributesBuilder::new() + .with_fixed_tpm(true) + .with_fixed_parent(true) + .with_sensitive_data_origin(true) + .with_user_with_auth(true) + .with_decrypt(true) + .with_sign_encrypt(false) + .with_restricted(true) + .build()?; + + let builder = tss_esapi::structures::PublicBuilder::new() + .with_object_attributes(object_attributes) + .with_name_hashing_algorithm(name_alg); + + match key_type { + "ecc" => builder + .with_public_algorithm(PublicAlgorithm::Ecc) + .with_ecc_parameters( + tss_esapi::structures::PublicEccParametersBuilder::new_restricted_decryption_key( + SymmetricDefinitionObject::AES_128_CFB, + EccCurve::NistP256, + ) + .build()?, + ) + .with_ecc_unique_identifier(Default::default()), + "rsa" => builder + .with_public_algorithm(PublicAlgorithm::Rsa) + .with_rsa_parameters( + tss_esapi::structures::PublicRsaParametersBuilder::new_restricted_decryption_key( + SymmetricDefinitionObject::AES_128_CFB, + tss_esapi::interface_types::key_bits::RsaKeyBits::Rsa2048, + tss_esapi::structures::RsaExponent::ZERO_EXPONENT, + ) + .build()?, + ) + .with_rsa_unique_identifier(Default::default()), + _ => return Err(eyre!("Unsupported key type used")), + } + .build() + .context("Error building public key") +} + +pub(super) fn create_tpm2b_public_sealed_object( + policy: Option, +) -> Result { + let mut object_attributes = ObjectAttributesBuilder::new() + .with_fixed_tpm(true) + .with_fixed_parent(true) + .with_no_da(true) + .with_admin_with_policy(true); + + if policy.is_none() { + object_attributes = object_attributes.with_user_with_auth(true); + } + let policy = match policy { + Some(p) => p, + None => Digest::try_from(vec![])?, + }; + + let mut params: tss_esapi::tss2_esys::TPMU_PUBLIC_PARMS = Default::default(); + params.keyedHashDetail.scheme.scheme = tss_constants::TPM2_ALG_NULL; + + Ok(tss_esapi::tss2_esys::TPM2B_PUBLIC { + size: std::mem::size_of::() as u16, + publicArea: tss_esapi::tss2_esys::TPMT_PUBLIC { + type_: tss_constants::TPM2_ALG_KEYEDHASH, + nameAlg: tss_constants::TPM2_ALG_SHA256, + objectAttributes: object_attributes.build()?.0, + authPolicy: tss_esapi::tss2_esys::TPM2B_DIGEST::from(policy), + parameters: params, + unique: Default::default(), + }, + }) +} + +pub(super) fn get_tpm2b_public(val: tss_esapi::tss2_esys::TPM2B_PUBLIC) -> Result> { + let mut offset = 0 as Sizedu; + let mut resp = Vec::with_capacity((val.size + 4) as usize); + + unsafe { + let res = tss_esapi::tss2_esys::Tss2_MU_TPM2B_PUBLIC_Marshal( + &val, + resp.as_mut_ptr(), + resp.capacity() as Sizedu, + &mut offset, + ); + if res != 0 { + bail!("Marshalling tpm2b_public failed"); + } + resp.set_len(offset as usize); + } + + Ok(resp) +} + +pub(super) fn get_tpm2b_private(val: tss_esapi::tss2_esys::TPM2B_PRIVATE) -> Result> { + let mut offset = 0 as Sizedu; + let mut resp = Vec::with_capacity((val.size + 4) as usize); + + unsafe { + let res = tss_esapi::tss2_esys::Tss2_MU_TPM2B_PRIVATE_Marshal( + &val, + resp.as_mut_ptr(), + resp.capacity() as Sizedu, + &mut offset, + ); + if res != 0 { + bail!("Marshalling tpm2b_private failed"); + } + resp.set_len(offset as usize); + } + + Ok(resp) +} + +pub(super) fn build_tpm2b_private(val: &[u8]) -> Result { + let mut resp = tss_esapi::tss2_esys::TPM2B_PRIVATE::default(); + let mut offset = 0 as Sizedu; + + unsafe { + let res = tss_esapi::tss2_esys::Tss2_MU_TPM2B_PRIVATE_Unmarshal( + val[..].as_ptr(), + val.len() as Sizedu, + &mut offset, + &mut resp, + ); + if res != 0 { + bail!("Unmarshalling tpm2b_private failed"); + } + } + + Ok(resp) +} + +pub(super) fn build_tpm2b_public(val: &[u8]) -> Result { + let mut resp = tss_esapi::tss2_esys::TPM2B_PUBLIC::default(); + let mut offset = 0 as Sizedu; + + unsafe { + let res = tss_esapi::tss2_esys::Tss2_MU_TPM2B_PUBLIC_Unmarshal( + val[..].as_ptr(), + val.len() as Sizedu, + &mut offset, + &mut resp, + ); + if res != 0 { + bail!("Unmarshalling tpm2b_public failed"); + } + } + + Ok(resp) +} diff --git a/src/tpm/utils.rs b/src/tpm/utils.rs new file mode 100644 index 0000000..dbeeaa2 --- /dev/null +++ b/src/tpm/utils.rs @@ -0,0 +1,110 @@ +use std::env; +use std::fs; +use std::str::FromStr; + +use base64::engine::general_purpose::URL_SAFE_NO_PAD; +use base64::Engine; +use color_eyre::eyre::WrapErr; +use color_eyre::Result; +use serde::Deserialize; +use tpm2_policy::{PublicKey, SignedPolicyList, TPMPolicyStep}; +use tss_esapi::{ + handles::KeyHandle, + interface_types::{algorithm::HashingAlgorithm, resource_handles::Hierarchy}, + structures::Public, + Context, Tcti, +}; + +pub(crate) fn get_authorized_policy_step( + policy_pubkey_path: &str, + policy_path: &Option, + policy_ref: &Option, +) -> Result { + let policy_ref = match policy_ref { + Some(policy_ref) => policy_ref.as_bytes().to_vec(), + None => vec![], + }; + + let signkey = { + let contents = + fs::read_to_string(policy_pubkey_path).context("Error reading policy signkey")?; + serde_json::from_str::(&contents) + .context("Error deserializing signing public key")? + }; + + let policies = match policy_path { + None => None, + Some(policy_path) => { + let contents = fs::read_to_string(policy_path).context("Error reading policy")?; + Some( + serde_json::from_str::(&contents) + .context("Error deserializing policy")?, + ) + } + }; + + Ok(TPMPolicyStep::Authorized { + signkey, + policy_ref, + policies, + next: Box::new(TPMPolicyStep::NoStep), + }) +} + +pub(crate) fn get_hash_alg_from_name(name: Option<&String>) -> HashingAlgorithm { + match name { + None => HashingAlgorithm::Sha256, + Some(val) => match val.to_lowercase().as_str() { + "sha1" => HashingAlgorithm::Sha1, + "sha256" => HashingAlgorithm::Sha256, + "sha384" => HashingAlgorithm::Sha384, + "sha512" => HashingAlgorithm::Sha512, + _ => panic!("Unsupported hash algo: {:?}", name), + }, + } +} + +pub(crate) fn serialize_as_base64_url_no_pad( + bytes: &[u8], + serializer: S, +) -> Result +where + S: serde::Serializer, +{ + serializer.serialize_str(&URL_SAFE_NO_PAD.encode(bytes)) +} + +pub(crate) fn deserialize_as_base64_url_no_pad<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + String::deserialize(deserializer).and_then(|string| { + URL_SAFE_NO_PAD + .decode(string) + .map_err(serde::de::Error::custom) + }) +} + +pub(crate) fn get_tpm2_ctx() -> Result { + let tcti_path = match env::var("TCTI") { + Ok(val) => val, + Err(_) => { + if std::path::Path::new("/dev/tpmrm0").exists() { + "device:/dev/tpmrm0".to_string() + } else { + "device:/dev/tpm0".to_string() + } + } + }; + + let tcti = Tcti::from_str(&tcti_path).context("Error parsing TCTI specification")?; + Context::new(tcti).context("Error initializing TPM2 context") +} + +pub(crate) fn get_tpm2_primary_key(ctx: &mut Context, pub_template: Public) -> Result { + ctx.execute_with_nullauth_session(|ctx| { + ctx.create_primary(Hierarchy::Owner, pub_template, None, None, None, None) + .map(|r| r.key_handle) + }) + .map_err(|e| e.into()) +} From 302625f2fb037f1ca91d53bdc0d6d9e5ebcf6a49 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 19 Dec 2023 10:21:13 +0100 Subject: [PATCH 07/20] add tries, and timeout --- flake.nix | 4 ++-- src/main.rs | 33 +++++++++++++++++++++++++-------- src/tpm/mod.rs | 5 ++--- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/flake.nix b/flake.nix index fdb20f9..7f6ee89 100644 --- a/flake.nix +++ b/flake.nix @@ -19,6 +19,7 @@ doCheck = false; + buildInputs = with pkgs; [ openssl tpm2-tss ]; nativeBuildInputs = with pkgs; [ llvmPackages.libclang llvmPackages.libcxxClang @@ -26,7 +27,6 @@ pkg-config ]; LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; - buildInputs = with pkgs; [ openssl tpm2-tss ]; preBuild = '' export BINDGEN_EXTRA_CLANG_ARGS="$(< ${stdenv.cc}/nix-support/libc-crt1-cflags) \ @@ -53,7 +53,7 @@ ''; }; - devShell = pkgs.mkShell { + devShells.default = pkgs.mkShell { shellHook = "${packages.default.preBuild}"; inherit (packages.default) nativeBuildInputs buildInputs LIBCLANG_PATH; }; diff --git a/src/main.rs b/src/main.rs index 54bcc66..9b0a758 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,8 @@ use std::io::{Read, Write}; use std::os::unix::net::UnixStream; use std::path::PathBuf; use std::process::exit; +use std::thread::sleep; +use std::time::Duration; use tpm::tpm_objects::TPM2Config; fn get_control_socket() -> Option { @@ -25,6 +27,7 @@ fn get_control_socket() -> Option { } #[derive(Debug, Clone, Copy)] +#[allow(dead_code)] enum ControlOp { Initialize = 0, Unlock = 1, @@ -52,6 +55,7 @@ impl ControlResult { match num { 0 => Some(Self::Ok), 1 => Some(Self::Denied), + 2 => Some(Self::Failed), 3 => Some(Self::NoDaemon), _ => None, } @@ -122,7 +126,14 @@ struct Cli { #[derive(Subcommand)] enum Commands { /// Unlock gnome keyring using encrypted password stored in tpm - Unlock, + Unlock { + #[arg(default_value_t = 5)] + tries: usize, + + // timeout in seconds + #[arg(default_value_t = 1)] + timeout: u64, + }, /// Enroll a password into the tpm to use when unlocking Enroll, } @@ -137,15 +148,21 @@ fn main() -> color_eyre::Result<()> { .ok_or_else(|| eyre!("Token path not found"))?; match cli.command { - Commands::Unlock => { + Commands::Unlock { tries, timeout } => { if token_path.exists() { let token = read_to_string(token_path)?; - let password = - tpm::perform_decrypt(token.as_bytes()).map_err(|err| eyre!("{err:?}"))?; - let res = unlock_keyring(password.as_slice())?; - if res != ControlResult::Ok { - eprintln!("Failed to unlock keyring: {res:?}"); - exit(2); + + for _ in 0..tries { + let password = + tpm::perform_decrypt(token.as_bytes()).map_err(|err| eyre!("{err:?}"))?; + let res = unlock_keyring(password.as_slice())?; + if res == ControlResult::Ok { + break; + } else { + eprintln!("Failed to unlock keyring: {res:?}"); + } + + sleep(Duration::from_secs(timeout)); } } else { bail!("password token file not found") diff --git a/src/tpm/mod.rs b/src/tpm/mod.rs index 6210eee..db01361 100644 --- a/src/tpm/mod.rs +++ b/src/tpm/mod.rs @@ -1,6 +1,5 @@ -// Copyright 2020 Patrick Uiterwijk -// -// Licensed under the MIT license +// Adapted from https://github.com/fedora-iot/clevis-pin-tpm2/ +// by Patrick Uiterwijk under the MIT License use std::convert::{TryFrom, TryInto}; From d249a67be47c1f50312d49f615ee9463daf43737 Mon Sep 17 00:00:00 2001 From: Vivian Roest Date: Tue, 19 Dec 2023 10:21:13 +0100 Subject: [PATCH 08/20] add tries, and timeout --- flake.nix | 4 ++-- src/main.rs | 33 +++++++++++++++++++++++++-------- src/tpm/mod.rs | 5 ++--- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/flake.nix b/flake.nix index fdb20f9..7f6ee89 100644 --- a/flake.nix +++ b/flake.nix @@ -19,6 +19,7 @@ doCheck = false; + buildInputs = with pkgs; [ openssl tpm2-tss ]; nativeBuildInputs = with pkgs; [ llvmPackages.libclang llvmPackages.libcxxClang @@ -26,7 +27,6 @@ pkg-config ]; LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; - buildInputs = with pkgs; [ openssl tpm2-tss ]; preBuild = '' export BINDGEN_EXTRA_CLANG_ARGS="$(< ${stdenv.cc}/nix-support/libc-crt1-cflags) \ @@ -53,7 +53,7 @@ ''; }; - devShell = pkgs.mkShell { + devShells.default = pkgs.mkShell { shellHook = "${packages.default.preBuild}"; inherit (packages.default) nativeBuildInputs buildInputs LIBCLANG_PATH; }; diff --git a/src/main.rs b/src/main.rs index 54bcc66..9b0a758 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,8 @@ use std::io::{Read, Write}; use std::os::unix::net::UnixStream; use std::path::PathBuf; use std::process::exit; +use std::thread::sleep; +use std::time::Duration; use tpm::tpm_objects::TPM2Config; fn get_control_socket() -> Option { @@ -25,6 +27,7 @@ fn get_control_socket() -> Option { } #[derive(Debug, Clone, Copy)] +#[allow(dead_code)] enum ControlOp { Initialize = 0, Unlock = 1, @@ -52,6 +55,7 @@ impl ControlResult { match num { 0 => Some(Self::Ok), 1 => Some(Self::Denied), + 2 => Some(Self::Failed), 3 => Some(Self::NoDaemon), _ => None, } @@ -122,7 +126,14 @@ struct Cli { #[derive(Subcommand)] enum Commands { /// Unlock gnome keyring using encrypted password stored in tpm - Unlock, + Unlock { + #[arg(default_value_t = 5)] + tries: usize, + + // timeout in seconds + #[arg(default_value_t = 1)] + timeout: u64, + }, /// Enroll a password into the tpm to use when unlocking Enroll, } @@ -137,15 +148,21 @@ fn main() -> color_eyre::Result<()> { .ok_or_else(|| eyre!("Token path not found"))?; match cli.command { - Commands::Unlock => { + Commands::Unlock { tries, timeout } => { if token_path.exists() { let token = read_to_string(token_path)?; - let password = - tpm::perform_decrypt(token.as_bytes()).map_err(|err| eyre!("{err:?}"))?; - let res = unlock_keyring(password.as_slice())?; - if res != ControlResult::Ok { - eprintln!("Failed to unlock keyring: {res:?}"); - exit(2); + + for _ in 0..tries { + let password = + tpm::perform_decrypt(token.as_bytes()).map_err(|err| eyre!("{err:?}"))?; + let res = unlock_keyring(password.as_slice())?; + if res == ControlResult::Ok { + break; + } else { + eprintln!("Failed to unlock keyring: {res:?}"); + } + + sleep(Duration::from_secs(timeout)); } } else { bail!("password token file not found") diff --git a/src/tpm/mod.rs b/src/tpm/mod.rs index 6210eee..db01361 100644 --- a/src/tpm/mod.rs +++ b/src/tpm/mod.rs @@ -1,6 +1,5 @@ -// Copyright 2020 Patrick Uiterwijk -// -// Licensed under the MIT license +// Adapted from https://github.com/fedora-iot/clevis-pin-tpm2/ +// by Patrick Uiterwijk under the MIT License use std::convert::{TryFrom, TryInto}; From e6e9f6a77078d50ef07933cdc39423966d0ff4ec Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 19 Dec 2023 10:29:17 +0100 Subject: [PATCH 09/20] bump version --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62d85a2..5fa1ae1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -403,7 +403,7 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "gnome-autounlock-keyring" -version = "0.1.0" +version = "0.1.1" dependencies = [ "base64 0.21.5", "clap", diff --git a/Cargo.toml b/Cargo.toml index 97be2f4..2380e83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gnome-autounlock-keyring" -version = "0.1.0" +version = "0.1.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 801e98fa1dc0c09fe145858bfee432093be736e9 Mon Sep 17 00:00:00 2001 From: Vivian Roest Date: Tue, 19 Dec 2023 10:29:17 +0100 Subject: [PATCH 10/20] bump version --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62d85a2..5fa1ae1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -403,7 +403,7 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "gnome-autounlock-keyring" -version = "0.1.0" +version = "0.1.1" dependencies = [ "base64 0.21.5", "clap", diff --git a/Cargo.toml b/Cargo.toml index 97be2f4..2380e83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gnome-autounlock-keyring" -version = "0.1.0" +version = "0.1.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 2f4637f362a7bb8171b5641435c6cd29b0d56157 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 19 Dec 2023 12:27:50 +0100 Subject: [PATCH 11/20] add module --- flake.nix | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 7f6ee89..42b04cc 100644 --- a/flake.nix +++ b/flake.nix @@ -55,7 +55,31 @@ devShells.default = pkgs.mkShell { shellHook = "${packages.default.preBuild}"; - inherit (packages.default) nativeBuildInputs buildInputs LIBCLANG_PATH; + inherit (packages.default) + nativeBuildInputs buildInputs LIBCLANG_PATH; }; - }); + }) // { + nixosModules = rec { + default = { config, lib, pkgs, ... }: + with lib; + let cfg = config.services.gnome-autounlock-keyring; + in { + options.services.gnome-autounlock-keyring = { + enable = mkEnableOption "gnome-autounlock.keyring"; + }; + + config = mkIf cfg.enable { + systemd.user.services.gnome-autounlock-keyring = { + description = "Automatically unlock gnome keyring using TPM"; + wantedBy = [ "gnome-session.target" ]; + script = '' + ${self.packages.${pkgs.system}.default}/bin/gnome-autounlock-keyring unlock + ''; + serviceConfig = { Type = "oneshot"; }; + }; + }; + }; + gnome-autounlock-keyring = default; + }; + }; } From c48faee903e108d5bc4d9463e6a5d5b16faf89f0 Mon Sep 17 00:00:00 2001 From: Vivian Roest Date: Tue, 19 Dec 2023 12:27:50 +0100 Subject: [PATCH 12/20] add module --- flake.nix | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 7f6ee89..42b04cc 100644 --- a/flake.nix +++ b/flake.nix @@ -55,7 +55,31 @@ devShells.default = pkgs.mkShell { shellHook = "${packages.default.preBuild}"; - inherit (packages.default) nativeBuildInputs buildInputs LIBCLANG_PATH; + inherit (packages.default) + nativeBuildInputs buildInputs LIBCLANG_PATH; }; - }); + }) // { + nixosModules = rec { + default = { config, lib, pkgs, ... }: + with lib; + let cfg = config.services.gnome-autounlock-keyring; + in { + options.services.gnome-autounlock-keyring = { + enable = mkEnableOption "gnome-autounlock.keyring"; + }; + + config = mkIf cfg.enable { + systemd.user.services.gnome-autounlock-keyring = { + description = "Automatically unlock gnome keyring using TPM"; + wantedBy = [ "gnome-session.target" ]; + script = '' + ${self.packages.${pkgs.system}.default}/bin/gnome-autounlock-keyring unlock + ''; + serviceConfig = { Type = "oneshot"; }; + }; + }; + }; + gnome-autounlock-keyring = default; + }; + }; } From 84a674f99848db0cd6b0af18ea6c085ae4853a03 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 19 Dec 2023 14:54:11 +0100 Subject: [PATCH 13/20] update gitignore --- .gitignore | 3 ++- result | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 120000 result diff --git a/.gitignore b/.gitignore index 1b91d7b..cf74362 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target .idea/ -.direnv \ No newline at end of file +.direnv +result \ No newline at end of file diff --git a/result b/result deleted file mode 120000 index b5c3c34..0000000 --- a/result +++ /dev/null @@ -1 +0,0 @@ -/nix/store/bwvjqn0n9xljxk646l0rqj6m0ym221m7-gnome-autounlock-keyring-0.1.0 \ No newline at end of file From 3c2daca7aacdefef4ce583482d1c85fc05c1c9a0 Mon Sep 17 00:00:00 2001 From: Vivian Roest Date: Tue, 19 Dec 2023 14:54:11 +0100 Subject: [PATCH 14/20] update gitignore --- .gitignore | 3 ++- result | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 120000 result diff --git a/.gitignore b/.gitignore index 1b91d7b..cf74362 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target .idea/ -.direnv \ No newline at end of file +.direnv +result \ No newline at end of file diff --git a/result b/result deleted file mode 120000 index b5c3c34..0000000 --- a/result +++ /dev/null @@ -1 +0,0 @@ -/nix/store/bwvjqn0n9xljxk646l0rqj6m0ym221m7-gnome-autounlock-keyring-0.1.0 \ No newline at end of file From db96c4a4ebc05c8e4f1b427074fc5fe61c026998 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 19 Dec 2023 14:59:04 +0100 Subject: [PATCH 15/20] minor bump --- Cargo.lock | 2 +- Cargo.toml | 2 +- flake.nix | 4 ++-- src/main.rs | 13 +++++++++---- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fa1ae1..9c4f75e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -403,7 +403,7 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "gnome-autounlock-keyring" -version = "0.1.1" +version = "0.2.0" dependencies = [ "base64 0.21.5", "clap", diff --git a/Cargo.toml b/Cargo.toml index 2380e83..5706c83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gnome-autounlock-keyring" -version = "0.1.1" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/flake.nix b/flake.nix index 42b04cc..34e4981 100644 --- a/flake.nix +++ b/flake.nix @@ -7,7 +7,7 @@ outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let - cargoToml = (builtins.fromTOML (builtins.readFile ./Cargo.toml)); + cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) stdenv lib; in rec { @@ -75,7 +75,7 @@ script = '' ${self.packages.${pkgs.system}.default}/bin/gnome-autounlock-keyring unlock ''; - serviceConfig = { Type = "oneshot"; }; + serviceConfig.Type = "oneshot"; }; }; }; diff --git a/src/main.rs b/src/main.rs index 9b0a758..16a5098 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,6 @@ use std::fs::{read_to_string, File}; use std::io::{Read, Write}; use std::os::unix::net::UnixStream; use std::path::PathBuf; -use std::process::exit; use std::thread::sleep; use std::time::Duration; use tpm::tpm_objects::TPM2Config; @@ -152,10 +151,12 @@ fn main() -> color_eyre::Result<()> { if token_path.exists() { let token = read_to_string(token_path)?; + let mut res = ControlResult::NoDaemon; + for _ in 0..tries { let password = tpm::perform_decrypt(token.as_bytes()).map_err(|err| eyre!("{err:?}"))?; - let res = unlock_keyring(password.as_slice())?; + res = unlock_keyring(password.as_slice())?; if res == ControlResult::Ok { break; } else { @@ -164,6 +165,11 @@ fn main() -> color_eyre::Result<()> { sleep(Duration::from_secs(timeout)); } + + if res != ControlResult::Ok { + bail!("Unlocking failed after {tries}: {res:?}"); + } + } else { bail!("password token file not found") } @@ -173,8 +179,7 @@ fn main() -> color_eyre::Result<()> { let password = rpassword::prompt_password("Password: ")?; if unlock_keyring(password.as_bytes())? != ControlResult::Ok { - eprintln!("invalid password"); - exit(3); + bail!("invalid password"); } let token = tpm::perform_encrypt(TPM2Config::default(), password.as_bytes()) From 84f3dd55ef87047ad8bb7f32b8af3b9a5955e2d6 Mon Sep 17 00:00:00 2001 From: Vivian Roest Date: Tue, 19 Dec 2023 14:59:04 +0100 Subject: [PATCH 16/20] minor bump --- Cargo.lock | 2 +- Cargo.toml | 2 +- flake.nix | 4 ++-- src/main.rs | 13 +++++++++---- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fa1ae1..9c4f75e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -403,7 +403,7 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "gnome-autounlock-keyring" -version = "0.1.1" +version = "0.2.0" dependencies = [ "base64 0.21.5", "clap", diff --git a/Cargo.toml b/Cargo.toml index 2380e83..5706c83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gnome-autounlock-keyring" -version = "0.1.1" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/flake.nix b/flake.nix index 42b04cc..34e4981 100644 --- a/flake.nix +++ b/flake.nix @@ -7,7 +7,7 @@ outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let - cargoToml = (builtins.fromTOML (builtins.readFile ./Cargo.toml)); + cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) stdenv lib; in rec { @@ -75,7 +75,7 @@ script = '' ${self.packages.${pkgs.system}.default}/bin/gnome-autounlock-keyring unlock ''; - serviceConfig = { Type = "oneshot"; }; + serviceConfig.Type = "oneshot"; }; }; }; diff --git a/src/main.rs b/src/main.rs index 9b0a758..16a5098 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,6 @@ use std::fs::{read_to_string, File}; use std::io::{Read, Write}; use std::os::unix::net::UnixStream; use std::path::PathBuf; -use std::process::exit; use std::thread::sleep; use std::time::Duration; use tpm::tpm_objects::TPM2Config; @@ -152,10 +151,12 @@ fn main() -> color_eyre::Result<()> { if token_path.exists() { let token = read_to_string(token_path)?; + let mut res = ControlResult::NoDaemon; + for _ in 0..tries { let password = tpm::perform_decrypt(token.as_bytes()).map_err(|err| eyre!("{err:?}"))?; - let res = unlock_keyring(password.as_slice())?; + res = unlock_keyring(password.as_slice())?; if res == ControlResult::Ok { break; } else { @@ -164,6 +165,11 @@ fn main() -> color_eyre::Result<()> { sleep(Duration::from_secs(timeout)); } + + if res != ControlResult::Ok { + bail!("Unlocking failed after {tries}: {res:?}"); + } + } else { bail!("password token file not found") } @@ -173,8 +179,7 @@ fn main() -> color_eyre::Result<()> { let password = rpassword::prompt_password("Password: ")?; if unlock_keyring(password.as_bytes())? != ControlResult::Ok { - eprintln!("invalid password"); - exit(3); + bail!("invalid password"); } let token = tpm::perform_encrypt(TPM2Config::default(), password.as_bytes()) From 9f4244b900a9bd7dd68f0e16d9d3ce281fd76c43 Mon Sep 17 00:00:00 2001 From: Vivian Date: Sun, 24 Mar 2024 12:04:24 +0100 Subject: [PATCH 17/20] make target configurable --- flake.lock | 12 ++++++------ flake.nix | 38 +++++++++++++++++++++++++------------- src/main.rs | 1 - 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/flake.lock b/flake.lock index 66a6297..2ebc417 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1702933230, - "narHash": "sha256-xi8AZ3noIXrgmKLR+ij+CeYFoUTKiQuTLL+aA7FRdRQ=", + "lastModified": 1711276414, + "narHash": "sha256-tID8RCOmMzws4SBrPkZ5oEIpoDaBtF+U2s0zEH6u8Ms=", "owner": "nixos", "repo": "nixpkgs", - "rev": "4e2c5373180ecd17e41e879420be69dc642a6349", + "rev": "11d9b9e97bcb5091c8414bbf9e50eb0d5cd745a9", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 34e4981..3f3dd50 100644 --- a/flake.nix +++ b/flake.nix @@ -5,7 +5,16 @@ }; outputs = { self, nixpkgs, flake-utils }: - flake-utils.lib.eachDefaultSystem (system: + let + buildInputs = pkgs: with pkgs; [ openssl tpm2-tss ]; + nativeBuildInputs = pkgs: + with pkgs; [ + llvmPackages.libclang + llvmPackages.libcxxClang + clang + pkg-config + ]; + in flake-utils.lib.eachDefaultSystem (system: let cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); pkgs = nixpkgs.legacyPackages.${system}; @@ -19,13 +28,8 @@ doCheck = false; - buildInputs = with pkgs; [ openssl tpm2-tss ]; - nativeBuildInputs = with pkgs; [ - llvmPackages.libclang - llvmPackages.libcxxClang - clang - pkg-config - ]; + buildInputs = buildInputs pkgs; + nativeBuildInputs = nativeBuildInputs pkgs; LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; preBuild = '' @@ -55,8 +59,8 @@ devShells.default = pkgs.mkShell { shellHook = "${packages.default.preBuild}"; - inherit (packages.default) - nativeBuildInputs buildInputs LIBCLANG_PATH; + buildInputs = buildInputs pkgs; + nativeBuildInputs = nativeBuildInputs pkgs; }; }) // { nixosModules = rec { @@ -66,16 +70,24 @@ in { options.services.gnome-autounlock-keyring = { enable = mkEnableOption "gnome-autounlock.keyring"; + + target = mkOption { + type = str; + default = "graphical-session.target"; + example = "hyprland-session.target"; + }; }; config = mkIf cfg.enable { systemd.user.services.gnome-autounlock-keyring = { description = "Automatically unlock gnome keyring using TPM"; - wantedBy = [ "gnome-session.target" ]; + wantedBy = [ cfg.target ]; script = '' - ${self.packages.${pkgs.system}.default}/bin/gnome-autounlock-keyring unlock + ${ + self.packages.${pkgs.system}.default + }/bin/gnome-autounlock-keyring unlock ''; - serviceConfig.Type = "oneshot"; + serviceConfig.Type = "oneshot"; }; }; }; diff --git a/src/main.rs b/src/main.rs index 16a5098..12da225 100644 --- a/src/main.rs +++ b/src/main.rs @@ -169,7 +169,6 @@ fn main() -> color_eyre::Result<()> { if res != ControlResult::Ok { bail!("Unlocking failed after {tries}: {res:?}"); } - } else { bail!("password token file not found") } From 048a68c029d38e6b64681e55cd03bbcf98f9314f Mon Sep 17 00:00:00 2001 From: Vivian Date: Sun, 24 Mar 2024 12:04:24 +0100 Subject: [PATCH 18/20] make target configurable --- flake.lock | 12 ++++++------ flake.nix | 38 +++++++++++++++++++++++++------------- src/main.rs | 1 - 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/flake.lock b/flake.lock index 66a6297..2ebc417 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1702933230, - "narHash": "sha256-xi8AZ3noIXrgmKLR+ij+CeYFoUTKiQuTLL+aA7FRdRQ=", + "lastModified": 1711276414, + "narHash": "sha256-tID8RCOmMzws4SBrPkZ5oEIpoDaBtF+U2s0zEH6u8Ms=", "owner": "nixos", "repo": "nixpkgs", - "rev": "4e2c5373180ecd17e41e879420be69dc642a6349", + "rev": "11d9b9e97bcb5091c8414bbf9e50eb0d5cd745a9", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 34e4981..3f3dd50 100644 --- a/flake.nix +++ b/flake.nix @@ -5,7 +5,16 @@ }; outputs = { self, nixpkgs, flake-utils }: - flake-utils.lib.eachDefaultSystem (system: + let + buildInputs = pkgs: with pkgs; [ openssl tpm2-tss ]; + nativeBuildInputs = pkgs: + with pkgs; [ + llvmPackages.libclang + llvmPackages.libcxxClang + clang + pkg-config + ]; + in flake-utils.lib.eachDefaultSystem (system: let cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); pkgs = nixpkgs.legacyPackages.${system}; @@ -19,13 +28,8 @@ doCheck = false; - buildInputs = with pkgs; [ openssl tpm2-tss ]; - nativeBuildInputs = with pkgs; [ - llvmPackages.libclang - llvmPackages.libcxxClang - clang - pkg-config - ]; + buildInputs = buildInputs pkgs; + nativeBuildInputs = nativeBuildInputs pkgs; LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; preBuild = '' @@ -55,8 +59,8 @@ devShells.default = pkgs.mkShell { shellHook = "${packages.default.preBuild}"; - inherit (packages.default) - nativeBuildInputs buildInputs LIBCLANG_PATH; + buildInputs = buildInputs pkgs; + nativeBuildInputs = nativeBuildInputs pkgs; }; }) // { nixosModules = rec { @@ -66,16 +70,24 @@ in { options.services.gnome-autounlock-keyring = { enable = mkEnableOption "gnome-autounlock.keyring"; + + target = mkOption { + type = str; + default = "graphical-session.target"; + example = "hyprland-session.target"; + }; }; config = mkIf cfg.enable { systemd.user.services.gnome-autounlock-keyring = { description = "Automatically unlock gnome keyring using TPM"; - wantedBy = [ "gnome-session.target" ]; + wantedBy = [ cfg.target ]; script = '' - ${self.packages.${pkgs.system}.default}/bin/gnome-autounlock-keyring unlock + ${ + self.packages.${pkgs.system}.default + }/bin/gnome-autounlock-keyring unlock ''; - serviceConfig.Type = "oneshot"; + serviceConfig.Type = "oneshot"; }; }; }; diff --git a/src/main.rs b/src/main.rs index 16a5098..12da225 100644 --- a/src/main.rs +++ b/src/main.rs @@ -169,7 +169,6 @@ fn main() -> color_eyre::Result<()> { if res != ControlResult::Ok { bail!("Unlocking failed after {tries}: {res:?}"); } - } else { bail!("password token file not found") } From 1017642795eb398881eff78a822621f2d2a93be7 Mon Sep 17 00:00:00 2001 From: Vivian Date: Mon, 25 Mar 2024 10:35:49 +0100 Subject: [PATCH 19/20] fix module --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 3f3dd50..6dd6af1 100644 --- a/flake.nix +++ b/flake.nix @@ -72,7 +72,7 @@ enable = mkEnableOption "gnome-autounlock.keyring"; target = mkOption { - type = str; + type = types.str; default = "graphical-session.target"; example = "hyprland-session.target"; }; From 06279869e7a26dc91ca3e3abb84a3508d7f977cb Mon Sep 17 00:00:00 2001 From: Vivian Date: Mon, 25 Mar 2024 10:35:49 +0100 Subject: [PATCH 20/20] fix module --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 3f3dd50..6dd6af1 100644 --- a/flake.nix +++ b/flake.nix @@ -72,7 +72,7 @@ enable = mkEnableOption "gnome-autounlock.keyring"; target = mkOption { - type = str; + type = types.str; default = "graphical-session.target"; example = "hyprland-session.target"; };