tpm2 working
This commit is contained in:
parent
83a4181842
commit
b36aa43b3a
8 changed files with 862 additions and 89 deletions
140
Cargo.lock
generated
140
Cargo.lock
generated
|
@ -80,17 +80,6 @@ version = "1.0.75"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
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]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -118,12 +107,6 @@ version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "base64"
|
|
||||||
version = "0.13.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.21.5"
|
version = "0.21.5"
|
||||||
|
@ -136,7 +119,7 @@ version = "0.66.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7"
|
checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.4.1",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
"clang-sys",
|
"clang-sys",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
@ -159,6 +142,12 @@ version = "0.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac"
|
checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
|
@ -240,20 +229,6 @@ version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
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]]
|
[[package]]
|
||||||
name = "color-eyre"
|
name = "color-eyre"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
|
@ -305,6 +280,27 @@ dependencies = [
|
||||||
"powerfmt",
|
"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]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
|
@ -382,6 +378,17 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
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]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.28.1"
|
version = "0.28.1"
|
||||||
|
@ -398,12 +405,16 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
name = "gnome-autounlock-keyring"
|
name = "gnome-autounlock-keyring"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"base64 0.21.5",
|
||||||
"clap",
|
"clap",
|
||||||
"clevis-pin-tpm2",
|
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
|
"dirs",
|
||||||
|
"josekit",
|
||||||
"rpassword",
|
"rpassword",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"tpm2-policy",
|
||||||
|
"tss-esapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -418,15 +429,6 @@ version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
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]]
|
[[package]]
|
||||||
name = "home"
|
name = "home"
|
||||||
version = "0.5.9"
|
version = "0.5.9"
|
||||||
|
@ -466,16 +468,17 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "josekit"
|
name = "josekit"
|
||||||
version = "0.7.4"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "16e84ea7acc05b40e2fe6fa02a54b3731323c77e6015c36749f0b10c4dbbc32f"
|
checksum = "5754487a088f527b1407df470db8e654e4064dccbbe1fe850e0773721e9962b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64 0.13.1",
|
"base64 0.21.5",
|
||||||
"flate2",
|
"flate2",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"openssl",
|
"openssl",
|
||||||
"regex",
|
"regex",
|
||||||
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time",
|
"time",
|
||||||
|
@ -509,6 +512,17 @@ dependencies = [
|
||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.12"
|
version = "0.4.12"
|
||||||
|
@ -613,7 +627,7 @@ version = "0.10.61"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45"
|
checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.4.1",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -645,6 +659,12 @@ dependencies = [
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "option-ext"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owo-colors"
|
name = "owo-colors"
|
||||||
version = "3.5.0"
|
version = "3.5.0"
|
||||||
|
@ -749,6 +769,26 @@ dependencies = [
|
||||||
"proc-macro2",
|
"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]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.2"
|
version = "1.10.2"
|
||||||
|
@ -826,7 +866,7 @@ version = "0.38.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
|
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.4.1",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
|
@ -1105,6 +1145,12 @@ version = "0.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
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]]
|
[[package]]
|
||||||
name = "which"
|
name = "which"
|
||||||
version = "4.4.2"
|
version = "4.4.2"
|
||||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -7,8 +7,15 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
color-eyre = "0.6.2"
|
color-eyre = "0.6.2"
|
||||||
serde_json = "1.0.108"
|
serde_json = "1"
|
||||||
serde = { version = "1.0.193", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
clevis-pin-tpm2 = { path = "../clevis-pin-tpm2" }
|
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
rpassword = "7.3.1"
|
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"
|
||||||
|
|
35
flake.nix
35
flake.nix
|
@ -7,11 +7,28 @@
|
||||||
outputs = { self, nixpkgs, flake-utils }:
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
flake-utils.lib.eachDefaultSystem (system:
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
let
|
let
|
||||||
|
cargoToml = (builtins.fromTOML (builtins.readFile ./Cargo.toml));
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
inherit (pkgs) stdenv lib;
|
inherit (pkgs) stdenv lib;
|
||||||
in {
|
in rec {
|
||||||
devShell = pkgs.mkShell {
|
packages.default = pkgs.rustPlatform.buildRustPackage {
|
||||||
shellHook = ''
|
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; [ openssl tpm2-tss ];
|
||||||
|
|
||||||
|
preBuild = ''
|
||||||
export BINDGEN_EXTRA_CLANG_ARGS="$(< ${stdenv.cc}/nix-support/libc-crt1-cflags) \
|
export BINDGEN_EXTRA_CLANG_ARGS="$(< ${stdenv.cc}/nix-support/libc-crt1-cflags) \
|
||||||
$(< ${stdenv.cc}/nix-support/libc-cflags) \
|
$(< ${stdenv.cc}/nix-support/libc-cflags) \
|
||||||
$(< ${stdenv.cc}/nix-support/cc-cflags) \
|
$(< ${stdenv.cc}/nix-support/cc-cflags) \
|
||||||
|
@ -34,13 +51,11 @@
|
||||||
} \
|
} \
|
||||||
"
|
"
|
||||||
'';
|
'';
|
||||||
nativeBuildInputs = with pkgs; [
|
};
|
||||||
llvmPackages.libclang
|
|
||||||
llvmPackages.libcxxClang
|
devShell = pkgs.mkShell {
|
||||||
clang
|
shellHook = "${packages.default.preBuild}";
|
||||||
];
|
inherit (packages.default) nativeBuildInputs buildInputs LIBCLANG_PATH;
|
||||||
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
|
||||||
buildInputs = with pkgs; [ libclang pkg-config openssl tpm2-tss ];
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
1
result
Symbolic link
1
result
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
/nix/store/bwvjqn0n9xljxk646l0rqj6m0ym221m7-gnome-autounlock-keyring-0.1.0
|
34
src/main.rs
34
src/main.rs
|
@ -1,12 +1,14 @@
|
||||||
|
mod tpm;
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use clevis_pin_tpm2::tpm_objects::TPM2Config;
|
|
||||||
use color_eyre::eyre::{bail, eyre, WrapErr};
|
use color_eyre::eyre::{bail, eyre, WrapErr};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::{read_to_string, File};
|
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::os::unix::net::UnixStream;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
use tpm::tpm_objects::TPM2Config;
|
||||||
|
|
||||||
fn get_control_socket() -> Option<PathBuf> {
|
fn get_control_socket() -> Option<PathBuf> {
|
||||||
let gnome_var = env::var("GNOME_KEYRING_CONTROL")
|
let gnome_var = env::var("GNOME_KEYRING_CONTROL")
|
||||||
|
@ -22,6 +24,7 @@ fn get_control_socket() -> Option<PathBuf> {
|
||||||
gnome_var.or(xdg_var)
|
gnome_var.or(xdg_var)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
enum ControlOp {
|
enum ControlOp {
|
||||||
Initialize = 0,
|
Initialize = 0,
|
||||||
Unlock = 1,
|
Unlock = 1,
|
||||||
|
@ -108,13 +111,19 @@ fn unlock_keyring(password: &[u8]) -> color_eyre::Result<ControlResult> {
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
|
/// Defaults to CONFIG_DIR/gnome-keyring.tpm2
|
||||||
|
#[arg(short, long)]
|
||||||
|
token_path: Option<PathBuf>,
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: Commands,
|
command: Commands,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
enum Commands {
|
enum Commands {
|
||||||
|
/// Unlock gnome keyring using encrypted password stored in tpm
|
||||||
Unlock,
|
Unlock,
|
||||||
|
/// Enroll a password into the tpm to use when unlocking
|
||||||
Enroll,
|
Enroll,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,13 +131,17 @@ fn main() -> color_eyre::Result<()> {
|
||||||
color_eyre::install().unwrap();
|
color_eyre::install().unwrap();
|
||||||
let cli = Cli::parse();
|
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 {
|
match cli.command {
|
||||||
Commands::Unlock => {
|
Commands::Unlock => {
|
||||||
let file = PathBuf::from("/home/vivian/.config/gnome_password.token");
|
if token_path.exists() {
|
||||||
if file.exists() {
|
let token = read_to_string(token_path)?;
|
||||||
let token = read_to_string(file)?;
|
let password =
|
||||||
let password = clevis_pin_tpm2::perform_decrypt(token.as_bytes())
|
tpm::perform_decrypt(token.as_bytes()).map_err(|err| eyre!("{err:?}"))?;
|
||||||
.map_err(|err| eyre!("{err:?}"))?;
|
|
||||||
let res = unlock_keyring(password.as_slice())?;
|
let res = unlock_keyring(password.as_slice())?;
|
||||||
if res != ControlResult::Ok {
|
if res != ControlResult::Ok {
|
||||||
eprintln!("Failed to unlock keyring: {res:?}");
|
eprintln!("Failed to unlock keyring: {res:?}");
|
||||||
|
@ -147,10 +160,9 @@ fn main() -> color_eyre::Result<()> {
|
||||||
exit(3);
|
exit(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
let token =
|
let token = tpm::perform_encrypt(TPM2Config::default(), password.as_bytes())
|
||||||
clevis_pin_tpm2::perform_encrypt(TPM2Config::default(), password.as_bytes())
|
|
||||||
.map_err(|err| eyre!("{err:?}"))?;
|
.map_err(|err| eyre!("{err:?}"))?;
|
||||||
let mut file = File::create("/home/vivian/.config/gnome_password.token")?;
|
let mut file = File::create(token_path)?;
|
||||||
file.write_all(token.as_bytes())?;
|
file.write_all(token.as_bytes())?;
|
||||||
println!("Password enrolled successfully")
|
println!("Password enrolled successfully")
|
||||||
}
|
}
|
||||||
|
|
216
src/tpm/mod.rs
Normal file
216
src/tpm/mod.rs
Normal file
|
@ -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<String> {
|
||||||
|
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<u8>,
|
||||||
|
#[serde(
|
||||||
|
deserialize_with = "utils::deserialize_as_base64_url_no_pad",
|
||||||
|
serialize_with = "utils::serialize_as_base64_url_no_pad"
|
||||||
|
)]
|
||||||
|
jwk_pub: Vec<u8>,
|
||||||
|
key: String,
|
||||||
|
|
||||||
|
// PCR Binding may be specified, may not
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pcr_bank: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pcr_ids: Option<String>,
|
||||||
|
|
||||||
|
// 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<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
policy_ref: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
policy_path: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tpm2Inner {
|
||||||
|
fn get_pcr_ids(&self) -> Option<Vec<u64>> {
|
||||||
|
Some(
|
||||||
|
self.pcr_ids
|
||||||
|
.as_ref()?
|
||||||
|
.split(',')
|
||||||
|
.map(|x| x.parse::<u64>().unwrap())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&Tpm2Inner> for TPMPolicyStep {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(cfg: &Tpm2Inner) -> Result<Self> {
|
||||||
|
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<Vec<u8>> {
|
||||||
|
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)
|
||||||
|
}
|
366
src/tpm/tpm_objects.rs
Normal file
366
src/tpm/tpm_objects.rs
Normal file
|
@ -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<String>,
|
||||||
|
pub key: Option<String>,
|
||||||
|
pub pcr_bank: Option<String>,
|
||||||
|
// PCR IDs can be passed in as comma-separated string or json array
|
||||||
|
pub pcr_ids: Option<serde_json::Value>,
|
||||||
|
pub pcr_digest: Option<String>,
|
||||||
|
// Whether to use a policy. If this is specified without pubkey path or policy path, they get set to defaults
|
||||||
|
pub use_policy: Option<bool>,
|
||||||
|
// Public key (in JSON format) for a wildcard policy that's possibly OR'd with the PCR one
|
||||||
|
pub policy_pubkey_path: Option<String>,
|
||||||
|
pub policy_ref: Option<String>,
|
||||||
|
pub policy_path: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&TPM2Config> for TPMPolicyStep {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(cfg: &TPM2Config) -> Result<Self> {
|
||||||
|
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<Vec<u64>> {
|
||||||
|
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<String> {
|
||||||
|
match &self.pcr_ids {
|
||||||
|
None => None,
|
||||||
|
Some(serde_json::Value::Array(vals)) => Some(
|
||||||
|
vals.iter()
|
||||||
|
.map(|x| x.as_u64().unwrap().to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(","),
|
||||||
|
),
|
||||||
|
_ => panic!("Unexpected type found for pcr_ids"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn normalize(mut self) -> Result<TPM2Config> {
|
||||||
|
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<serde_json::Value> = 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<Vec<serde_json::Value>, _> = vals
|
||||||
|
.iter()
|
||||||
|
.map(|x| match x {
|
||||||
|
serde_json::Value::String(val) => {
|
||||||
|
match val.trim().parse::<serde_json::Number>() {
|
||||||
|
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<Public> {
|
||||||
|
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<Digest>,
|
||||||
|
) -> Result<tss_esapi::tss2_esys::TPM2B_PUBLIC> {
|
||||||
|
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::<tss_esapi::tss2_esys::TPMT_PUBLIC>() 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<Vec<u8>> {
|
||||||
|
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<Vec<u8>> {
|
||||||
|
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<tss_esapi::tss2_esys::TPM2B_PRIVATE> {
|
||||||
|
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<tss_esapi::tss2_esys::TPM2B_PUBLIC> {
|
||||||
|
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)
|
||||||
|
}
|
110
src/tpm/utils.rs
Normal file
110
src/tpm/utils.rs
Normal file
|
@ -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<String>,
|
||||||
|
policy_ref: &Option<String>,
|
||||||
|
) -> Result<TPMPolicyStep> {
|
||||||
|
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::<PublicKey>(&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::<SignedPolicyList>(&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<S>(
|
||||||
|
bytes: &[u8],
|
||||||
|
serializer: S,
|
||||||
|
) -> Result<S::Ok, S::Error>
|
||||||
|
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<Vec<u8>, 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<tss_esapi::Context> {
|
||||||
|
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<KeyHandle> {
|
||||||
|
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())
|
||||||
|
}
|
Loading…
Reference in a new issue