From a601c0e6fb1c67d6fa7764e37d8fa802beba4ab9 Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Thu, 19 Oct 2023 23:11:34 +0000 Subject: [PATCH] Migrate tpm2 code to rust --- .github/workflows/test.yml | 5 +- .gitignore | 1 + .gitlab-ci.yml | 6 +- Cargo.lock | 519 +++++++++++++++++- Cargo.toml | 4 + Readme.md | 8 + src/ed25519.rs | 51 +- src/error.rs | 1 + src/main.rs | 106 +--- src/message.rs | 71 ++- src/sign.rs | 135 +++++ src/test_common.rs | 14 + src/tpm2.rs | 140 ----- src/tpm2/common.rs | 429 +++++++++++++++ src/tpm2/mod.rs | 5 + src/tpm2/sign.rs | 400 ++++++++++++++ tests/files/message/sign/{key => key_ed25519} | 0 .../message/sign/{result => result_ed25519} | Bin tests/files/message/sign/tpm2/ecc.priv | Bin 0 -> 128 bytes tests/files/message/sign/tpm2/ecc.pub | Bin 0 -> 120 bytes tests/files/message/sign/tpm2/pcrs | 1 + tests/files/message/sign/tpm2/public_key.der | Bin 0 -> 91 bytes tests/files/message/sign/tpm2/public_key.pem | 4 + tests/files/message/sign/tpm2/public_key.raw | 1 + tests/files/sign/ed25519 | 1 + tests/files/sign/tpm2/ecc.priv | Bin 0 -> 128 bytes tests/files/sign/tpm2/ecc.pub | Bin 0 -> 120 bytes tests/files/sign/tpm2/pcrs | 1 + tests/files/sign/tpm2/public_key.der | Bin 0 -> 91 bytes tests/files/sign/tpm2/public_key.pem | 4 + tests/files/sign/tpm2/public_key.raw | 1 + tests/files/tpm2/common/public/ecc.pub | Bin 0 -> 88 bytes tests/files/tpm2/read_id/id.bin | 2 - tests/files/tpm2/read_id/sig.bin | 1 - .../files/tpm2/sign/read_pcr_file/empty/pcrs | 0 .../tpm2/sign/read_pcr_file/multiple/pcrs | 3 + .../files/tpm2/sign/read_pcr_file/seven/pcrs | 1 + .../tpm2/sign/read_private_file/ecc.priv | Bin 0 -> 128 bytes .../files/tpm2/sign/read_public_file/ecc.pub | Bin 0 -> 88 bytes tests/files/tpm2/sign/sign/handle/handle | Bin 0 -> 132 bytes .../tpm2/sign/sign/handle/public_key.der | Bin 0 -> 91 bytes .../tpm2/sign/sign/handle/public_key.pem | 4 + .../tpm2/sign/sign/handle/public_key.raw | 1 + .../tpm2/sign/sign/handle_pcr_5_6_7/handle | Bin 0 -> 164 bytes .../tpm2/sign/sign/handle_pcr_5_6_7/pcrs | 1 + .../sign/sign/handle_pcr_5_6_7/public_key.der | Bin 0 -> 91 bytes .../sign/sign/handle_pcr_5_6_7/public_key.pem | 4 + .../sign/sign/handle_pcr_5_6_7/public_key.raw | 1 + tests/files/tpm2/sign/sign/pcr_4_7/ecc.priv | Bin 0 -> 128 bytes tests/files/tpm2/sign/sign/pcr_4_7/ecc.pub | Bin 0 -> 120 bytes tests/files/tpm2/sign/sign/pcr_4_7/pcrs | 1 + .../tpm2/sign/sign/pcr_4_7/public_key.der | Bin 0 -> 91 bytes .../tpm2/sign/sign/pcr_4_7/public_key.pem | 4 + .../tpm2/sign/sign/pcr_4_7/public_key.raw | 1 + tests/files/tpm2/sign/sign/plain/ecc.priv | Bin 0 -> 128 bytes tests/files/tpm2/sign/sign/plain/ecc.pub | Bin 0 -> 88 bytes .../files/tpm2/sign/sign/plain/public_key.der | Bin 0 -> 91 bytes .../files/tpm2/sign/sign/plain/public_key.pem | 4 + .../files/tpm2/sign/sign/plain/public_key.raw | 1 + tests/run_test.sh | 3 + tests/run_with_test_tpm2.sh | 41 ++ tests/swtpm/tpm2-00.permall | Bin 0 -> 2731 bytes usr/bin/cryptographic_id_add_initramfs | 3 +- usr/lib/cryptographic_id/initramfs_helper | 18 +- usr/lib/cryptographic_id/show_identities | 75 +-- .../90cryptographic-id/module-setup.sh | 1 + usr/lib/initcpio/install/cryptographic-id | 1 + .../systemd/system/cryptographic_id.service | 3 +- .../system/cryptographic_id_helper@.service | 5 + 69 files changed, 1730 insertions(+), 357 deletions(-) create mode 100644 src/error.rs create mode 100644 src/sign.rs create mode 100644 src/test_common.rs delete mode 100644 src/tpm2.rs create mode 100644 src/tpm2/common.rs create mode 100644 src/tpm2/mod.rs create mode 100644 src/tpm2/sign.rs rename tests/files/message/sign/{key => key_ed25519} (100%) rename tests/files/message/sign/{result => result_ed25519} (100%) create mode 100644 tests/files/message/sign/tpm2/ecc.priv create mode 100644 tests/files/message/sign/tpm2/ecc.pub create mode 100644 tests/files/message/sign/tpm2/pcrs create mode 100644 tests/files/message/sign/tpm2/public_key.der create mode 100644 tests/files/message/sign/tpm2/public_key.pem create mode 100644 tests/files/message/sign/tpm2/public_key.raw create mode 100644 tests/files/sign/ed25519 create mode 100644 tests/files/sign/tpm2/ecc.priv create mode 100644 tests/files/sign/tpm2/ecc.pub create mode 100644 tests/files/sign/tpm2/pcrs create mode 100644 tests/files/sign/tpm2/public_key.der create mode 100644 tests/files/sign/tpm2/public_key.pem create mode 100644 tests/files/sign/tpm2/public_key.raw create mode 100644 tests/files/tpm2/common/public/ecc.pub delete mode 100644 tests/files/tpm2/read_id/id.bin delete mode 100644 tests/files/tpm2/read_id/sig.bin create mode 100644 tests/files/tpm2/sign/read_pcr_file/empty/pcrs create mode 100644 tests/files/tpm2/sign/read_pcr_file/multiple/pcrs create mode 100644 tests/files/tpm2/sign/read_pcr_file/seven/pcrs create mode 100644 tests/files/tpm2/sign/read_private_file/ecc.priv create mode 100644 tests/files/tpm2/sign/read_public_file/ecc.pub create mode 100644 tests/files/tpm2/sign/sign/handle/handle create mode 100644 tests/files/tpm2/sign/sign/handle/public_key.der create mode 100644 tests/files/tpm2/sign/sign/handle/public_key.pem create mode 100644 tests/files/tpm2/sign/sign/handle/public_key.raw create mode 100644 tests/files/tpm2/sign/sign/handle_pcr_5_6_7/handle create mode 100644 tests/files/tpm2/sign/sign/handle_pcr_5_6_7/pcrs create mode 100644 tests/files/tpm2/sign/sign/handle_pcr_5_6_7/public_key.der create mode 100644 tests/files/tpm2/sign/sign/handle_pcr_5_6_7/public_key.pem create mode 100644 tests/files/tpm2/sign/sign/handle_pcr_5_6_7/public_key.raw create mode 100644 tests/files/tpm2/sign/sign/pcr_4_7/ecc.priv create mode 100644 tests/files/tpm2/sign/sign/pcr_4_7/ecc.pub create mode 100644 tests/files/tpm2/sign/sign/pcr_4_7/pcrs create mode 100644 tests/files/tpm2/sign/sign/pcr_4_7/public_key.der create mode 100644 tests/files/tpm2/sign/sign/pcr_4_7/public_key.pem create mode 100644 tests/files/tpm2/sign/sign/pcr_4_7/public_key.raw create mode 100644 tests/files/tpm2/sign/sign/plain/ecc.priv create mode 100644 tests/files/tpm2/sign/sign/plain/ecc.pub create mode 100644 tests/files/tpm2/sign/sign/plain/public_key.der create mode 100644 tests/files/tpm2/sign/sign/plain/public_key.pem create mode 100644 tests/files/tpm2/sign/sign/plain/public_key.raw create mode 100755 tests/run_test.sh create mode 100755 tests/run_with_test_tpm2.sh create mode 100644 tests/swtpm/tpm2-00.permall diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8cd2755..87de06b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,10 @@ jobs: - run: git submodule update --init --recursive - run: sudo apt-get update - run: sudo apt-get --yes install protobuf-compiler cargo shellcheck + dbus-daemon + swtpm swtpm-tools tpm2-abrmd tpm2-tools + libtss2-dev libtss2-tcti-swtpm0 libtss2-tcti-tabrmd0 - run: shellcheck usr/lib/cryptographic_id/* usr/bin/* usr/lib/initcpio/install/cryptographic-id usr/lib/dracut/modules.d/90cryptographic-id/module-setup.sh - - run: cargo test --workspace --verbose --locked + - run: dbus-run-session bash tests/run_test.sh diff --git a/.gitignore b/.gitignore index ea8c4bf..eca2b99 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/tests/swtpm/.lock diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 123f673..38fd62e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,12 +2,14 @@ test: image: rust:latest script: - apt-get update - - apt-get --yes install protobuf-compiler shellcheck + - apt-get --yes install protobuf-compiler shellcheck dbus-daemon + swtpm swtpm-tools tpm2-abrmd tpm2-tools + libtss2-dev libtss2-tcti-tabrmd0 libtss2-tcti-swtpm0 - git submodule update --init --recursive - shellcheck usr/lib/cryptographic_id/* usr/bin/* usr/lib/initcpio/install/cryptographic-id usr/lib/dracut/modules.d/90cryptographic-id/module-setup.sh - - cargo test --workspace --verbose --locked + - dbus-run-session bash tests/run_test.sh codespell: image: ubuntu diff --git a/Cargo.lock b/Cargo.lock index bbbbc99..710bd21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,56 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.21.4" @@ -29,6 +79,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + [[package]] name = "bitflags" version = "1.3.2" @@ -83,6 +139,18 @@ dependencies = [ "libc", ] +[[package]] +name = "crypto-bigint" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -97,13 +165,17 @@ dependencies = [ name = "cryptographic-id-rs" version = "0.2.2" dependencies = [ + "asn1-rs", "base64", "ed25519-dalek", + "p256", "prost", "prost-build", "qrcode", "rand", + "sha2", "tempfile", + "tss-esapi", ] [[package]] @@ -118,7 +190,7 @@ dependencies = [ "digest", "fiat-crypto", "platforms", - "rustc_version", + "rustc_version 0.4.0", "subtle", "zeroize", ] @@ -131,7 +203,7 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -141,6 +213,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] @@ -151,7 +224,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", ] [[package]] @@ -184,6 +284,46 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "elliptic-curve" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[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 2.0.38", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -206,6 +346,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "fiat-crypto" version = "0.2.1" @@ -226,6 +376,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -239,6 +390,17 @@ dependencies = [ "wasi", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "hashbrown" version = "0.14.1" @@ -251,6 +413,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "home" version = "0.5.5" @@ -260,6 +431,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "hostname-validator" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" + [[package]] name = "indexmap" version = "2.0.2" @@ -297,24 +474,112 @@ 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 0.3.3", + "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 = "multimap" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[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.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[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.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pest" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + [[package]] name = "petgraph" version = "0.6.4" @@ -325,6 +590,41 @@ dependencies = [ "indexmap", ] +[[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", + "oid", + "picky-asn1", + "picky-asn1-der", + "serde", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -335,6 +635,12 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + [[package]] name = "platforms" version = "3.1.2" @@ -354,7 +660,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.38", +] + +[[package]] +name = "primeorder" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c2fcef82c0ec6eefcc179b978446c399b3cdf73c392c35604e399eee6df1ee3" +dependencies = [ + "elliptic-curve", ] [[package]] @@ -393,7 +708,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn", + "syn 2.0.38", "tempfile", "which", ] @@ -408,7 +723,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -506,20 +821,48 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver", + "semver 1.0.20", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", ] [[package]] name = "rustix" -version = "0.38.19" +version = "0.38.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" +checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" dependencies = [ "bitflags 2.4.1", "errno", @@ -528,12 +871,44 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[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" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +[[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.189" @@ -543,6 +918,15 @@ 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.189" @@ -551,7 +935,7 @@ checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", ] [[package]] @@ -570,6 +954,10 @@ name = "signature" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest", + "rand_core", +] [[package]] name = "spki" @@ -581,12 +969,29 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "subtle" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.38" @@ -598,6 +1003,24 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "target-lexicon" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" + [[package]] name = "tempfile" version = "3.8.0" @@ -611,18 +1034,82 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "thiserror" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "tss-esapi" +version = "7.1.0" +source = "git+https://github.com/parallaxsecond/rust-tss-esapi.git?rev=21d426354a8f3cf40f3727388f9c74cd8828e0cd#21d426354a8f3cf40f3727388f9c74cd8828e0cd" +dependencies = [ + "bitfield", + "cfg-if", + "enumflags2", + "hostname-validator", + "log", + "mbox", + "num-derive", + "num-traits", + "oid", + "picky-asn1", + "picky-asn1-x509", + "regex", + "semver 1.0.20", + "serde", + "tss-esapi-sys", + "zeroize", +] + +[[package]] +name = "tss-esapi-sys" +version = "0.4.0" +source = "git+https://github.com/parallaxsecond/rust-tss-esapi.git?rev=21d426354a8f3cf40f3727388f9c74cd8828e0cd#21d426354a8f3cf40f3727388f9c74cd8828e0cd" +dependencies = [ + "pkg-config", + "target-lexicon", +] + [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[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 = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "version_check" version = "0.9.4" @@ -718,3 +1205,17 @@ name = "zeroize" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +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 2.0.38", +] diff --git a/Cargo.toml b/Cargo.toml index f82c1d8..2ec48ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,14 +5,18 @@ authors = ["Simon Brand "] edition = "2021" [dependencies] +asn1-rs = "0.5" base64 = "0.21" ed25519-dalek = { version = "2.0", features = ["rand_core"] } prost = "0.12" qrcode = { version = "0.12", default-features = false } rand = "0.8" +sha2 = "0.10" +tss-esapi = { git = "https://github.com/parallaxsecond/rust-tss-esapi.git", rev = "21d426354a8f3cf40f3727388f9c74cd8828e0cd" } [build-dependencies] prost-build = "0.12" [dev-dependencies] +p256 = "0.13" tempfile = "3.6" diff --git a/Readme.md b/Readme.md index 7ac3cca..314ed98 100644 --- a/Readme.md +++ b/Readme.md @@ -68,6 +68,14 @@ target/debug/cryptographic-id-rs show path/to/testkey target/debug/cryptographic-id-rs sign path/to/testkey Message ``` +### Tests + +To run the tests, you need swtpm and tpm2-abrmd installed. + +```bash +dbus-run-session bash tests/run_test.sh +``` + ### Security The rust part of this tool is completely sandboxed using systemd. Please review this project and it's dependencies, but it should be enough to review the shell-scripts and the systemd-service files, if you don't have enough time and don't trust this project or it's dependencies. diff --git a/src/ed25519.rs b/src/ed25519.rs index 7151748..499dfb4 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -2,13 +2,15 @@ use std::array::TryFromSliceError; use std::io; use std::path::PathBuf; -use ed25519_dalek::Signature; -use ed25519_dalek::Signer; pub use ed25519_dalek::SigningKey; -use ed25519_dalek::VerifyingKey; +#[cfg(test)] +use ed25519_dalek::{Signature, Verifier}; +use ed25519_dalek::{Signer, VerifyingKey}; use rand::rngs::OsRng; use crate::conv; +#[cfg(test)] +use crate::error::DynError; use crate::fs; pub fn create_keypair() -> SigningKey { @@ -32,14 +34,21 @@ pub fn format_verifying_key(key: &VerifyingKey) -> String { .join("\n"); } -fn sign(keypair: &SigningKey, message: &[u8]) -> Signature { +pub fn sign(keypair: &SigningKey, message: &[u8]) -> Vec { let signature = keypair.sign(message); - return signature; + return signature.to_bytes().to_vec(); } -pub fn sign_array(keypair: &SigningKey, to_sign_arr: &Vec>) -> Vec { - let to_sign = conv::flatten_binary_vec(&to_sign_arr); - return sign(keypair, &to_sign).to_bytes().to_vec(); +#[cfg(test)] +pub fn verify( + verifying_key: &[u8], + message: &[u8], + signature: &[u8], +) -> Result<(), DynError> { + let key = VerifyingKey::from_bytes(verifying_key.try_into()?)?; + let sig = Signature::from_bytes(signature.try_into()?); + key.verify(message, &sig)?; + return Ok(()); } pub fn save_keypair_to_file( @@ -67,6 +76,7 @@ pub fn load_keypair_from_file(filename: &PathBuf) -> io::Result { #[cfg(test)] mod tests { + use ed25519_dalek::Signature; use tempfile; fn load_test_key() -> super::SigningKey { @@ -83,7 +93,8 @@ mod tests { let key = super::create_keypair(); let data = vec![5, 72, 24, 82, 23, 92, 24, 38, 151, 45, 2]; let sig = super::sign(&key, &data); - key.verify(&data, &sig).unwrap(); + let sig_arr: [u8; 64] = sig.try_into().unwrap(); + key.verify(&data, &Signature::from_bytes(&sig_arr)).unwrap(); } #[test] @@ -102,27 +113,7 @@ mod tests { fn sign() { let key = load_test_key(); let data = vec![72, 24, 12, 23, 22, 29, 98, 151, 45, 180]; - let signature = super::sign(&key, &data).to_bytes().to_vec(); - assert_eq!( - signature, - super::fs::read_file(&super::fs::to_path_buf( - "tests/files/ed25519/signature" - )) - .unwrap() - ); - } - - #[test] - fn sign_array() { - let key = load_test_key(); - let data = vec![ - vec![72, 24], - vec![], - vec![12, 23, 22], - vec![29], - vec![98, 151, 45, 180], - ]; - let signature = super::sign_array(&key, &data); + let signature = super::sign(&key, &data); assert_eq!( signature, super::fs::read_file(&super::fs::to_path_buf( diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..16d5274 --- /dev/null +++ b/src/error.rs @@ -0,0 +1 @@ +pub type DynError = Box; diff --git a/src/main.rs b/src/main.rs index dc22e95..d560371 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,19 +3,21 @@ use std::path::PathBuf; mod conv; mod ed25519; +mod error; mod fs; mod message; mod qrcode; +mod sign; mod time; mod tpm2; use message::cryptographic_id::PublicKeyType; +#[cfg(test)] +mod test_common; enum Action { CreateKey(PathBuf), ShowPublicKey(PathBuf), SignWithKey(PathBuf, String), - TPM2Build(PathBuf, PathBuf, PathBuf), - TPM2Show(PathBuf, PathBuf), } fn print_help() { @@ -54,18 +56,6 @@ fn parse_args(args: &Vec) -> Result { key_path, msg.to_string(), )); - } else if action == "tpm2_show" { - let out_path = fs::to_path_buf(&args[3]); - return Ok(Action::TPM2Show(key_path, out_path)); - } - } - if args.len() == 5 { - if action == "tpm2_build" { - let msg_path = fs::to_path_buf(&args[3]); - let out_path = fs::to_path_buf(&args[4]); - return Ok(Action::TPM2Build( - key_path, msg_path, out_path, - )); } } return Err(()); @@ -96,26 +86,27 @@ fn parse_args_and_execute(args: &Vec) -> i32 { }; } Action::ShowPublicKey(path) => { - let keypair = - match ed25519::load_keypair_from_file(&path) { - Ok(k) => k, - Err(e) => { - println!( - "Error loading key: {}", - e - ); - return 2; - } - }; - let hex = ed25519::format_verifying_key( - &keypair.verifying_key(), - ); + let sign_config = match sign::SigningConfig::load(&path) + { + Ok(k) => k, + Err(e) => { + println!("Error loading key: {}", e); + return 2; + } + }; + let hex = match sign_config.fingerprint() { + Ok(k) => k, + Err(e) => { + println!("Error formatting key: {}", e); + return 2; + } + }; println!("Public Key:\n{}", hex); return 0; } Action::SignWithKey(path, msg) => { - let keypair = - match ed25519::load_keypair_from_file(&path) { + let mut sign_config = + match sign::SigningConfig::load(&path) { Ok(k) => k, Err(e) => { println!( @@ -127,63 +118,20 @@ fn parse_args_and_execute(args: &Vec) -> i32 { }; let timestamp = time::now(); let mut msg = message::CryptographicId { - public_key: keypair - .verifying_key() - .to_bytes() - .to_vec(), + public_key: vec![], timestamp: timestamp, msg: msg.as_bytes().to_vec(), public_key_type: PublicKeyType::Ed25519 as i32, signature: Vec::new(), personal_information: Vec::new(), }; - message::sign(&mut msg, &keypair); - let data = match message::to_data(&msg) { - Ok(d) => d, - Err(e) => { - println!( - "Error while encoding \ - message: {}", - e - ); - return 3; - } - }; - return match qrcode::as_string(&data) { - Ok(s) => { - println!("{}", s); - 0 - } - Err(e) => { - println!( - "Error while encoding \ - qrcode: {}", - e - ); - 4 - } - }; - } - Action::TPM2Build(key_path, msg_path, out_path) => { - let timestamp = time::now(); - return match tpm2::build( - &key_path, &msg_path, timestamp, &out_path, - ) { - Ok(_) => 0, + match message::sign(&mut msg, &mut sign_config) { Err(e) => { - println!("Error while i/o: {}", e); - 5 + println!("Error while signing: {}", e); + return 2; } - }; - } - Action::TPM2Show(sig_path, out_path) => { - let msg = match tpm2::read_id(&sig_path, &out_path) { - Ok(d) => d, - Err(e) => { - println!("Error while i/o: {}", e); - return 5; - } - }; + _ => {} + } let data = match message::to_data(&msg) { Ok(d) => d, Err(e) => { diff --git a/src/message.rs b/src/message.rs index a0d4e99..fa20c07 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,4 +1,7 @@ -use crate::ed25519; +use crate::conv; +use crate::sign::SigningConfig; +#[cfg(test)] +use crate::test_common::verify_p256; use prost::Message; include!(concat!(env!("OUT_DIR"), "/cryptographic_id.rs")); @@ -30,20 +33,43 @@ fn personal_info_to_sign_arr( .to_vec(); } -pub fn sign(data: &mut CryptographicId, keypair: &ed25519::SigningKey) { +pub fn sign( + data: &mut CryptographicId, + sign_config: &mut SigningConfig, +) -> Result<(), Box> { + data.public_key = sign_config.public_key()?; + data.public_key_type = sign_config.public_key_type().into(); let to_sign_arr = to_sign_arr(&data); - data.signature = ed25519::sign_array(&keypair, &to_sign_arr); + let sign_data = conv::flatten_binary_vec(&to_sign_arr); + data.signature = sign_config.sign(&sign_data)?; for e in &mut data.personal_information { let e_to_sign_arr = personal_info_to_sign_arr(&e); - e.signature = - ed25519::sign_array(&keypair, &e_to_sign_arr.to_vec()); + let e_sign_data = conv::flatten_binary_vec(&e_to_sign_arr); + e.signature = sign_config.sign(&e_sign_data)?; + } + return Ok(()); +} + +#[cfg(test)] +pub fn verify( + data: &CryptographicId, +) -> Result<(), Box> { + let to_sign_arr = to_sign_arr(&data); + let sign_data = conv::flatten_binary_vec(&to_sign_arr); + verify_p256(&data.public_key, &sign_data, &data.signature)?; + + for e in &data.personal_information { + let e_to_sign_arr = personal_info_to_sign_arr(&e); + let e_sign_data = conv::flatten_binary_vec(&e_to_sign_arr); + verify_p256(&data.public_key, &e_sign_data, &e.signature)?; } + return Ok(()); } #[cfg(test)] mod tests { - use crate::ed25519; + use crate::error::DynError; use crate::fs; use crate::message; use message::cryptographic_id::PersonalInformation; @@ -125,18 +151,39 @@ mod tests { } #[test] - fn sign() { - let key = ed25519::load_keypair_from_file(&fs::to_path_buf( - "tests/files/message/sign/key", + fn sign_ed25519() { + let mut key = super::SigningConfig::load(&fs::to_path_buf( + "tests/files/message/sign/key_ed25519", )) .unwrap(); let mut msg = example_id(); - msg.public_key = key.verifying_key().to_bytes().to_vec(); - message::sign(&mut msg, &key); + message::sign(&mut msg, &mut key).unwrap(); let exp_result = fs::read_file(&fs::to_path_buf( - "tests/files/message/sign/result", + "tests/files/message/sign/result_ed25519", )) .unwrap(); assert_eq!(super::to_data(&msg).unwrap(), exp_result); } + + #[test] + fn sign_tpm2() -> Result<(), DynError> { + let mut key = super::SigningConfig::load(&fs::to_path_buf( + "tests/files/message/sign/tpm2", + ))?; + let mut msg = example_id(); + message::sign(&mut msg, &mut key)?; + message::verify(&msg)?; + assert_eq!(msg.public_key, key.public_key()?); + assert_eq!(msg.public_key_type, key.public_key_type() as i32); + // verify only signature and public key changed + msg.signature = example_id().signature; + msg.public_key = example_id().public_key; + msg.public_key_type = example_id().public_key_type; + msg.personal_information[0].signature = + example_id().personal_information[0].signature.clone(); + msg.personal_information[1].signature = + example_id().personal_information[1].signature.clone(); + assert_eq!(msg, example_id()); + return Ok(()); + } } diff --git a/src/sign.rs b/src/sign.rs new file mode 100644 index 0000000..8d55d56 --- /dev/null +++ b/src/sign.rs @@ -0,0 +1,135 @@ +use crate::ed25519; +pub use crate::ed25519::SigningKey; +use crate::error::DynError; +use crate::message::cryptographic_id::PublicKeyType; +use crate::tpm2::Tpm2SigningConfig; +use std::io; +use std::path::PathBuf; + +pub enum SigningConfig { + Ed25519(SigningKey), + Tpm2(Tpm2SigningConfig), +} + +impl SigningConfig { + pub fn load(pathbuf: &PathBuf) -> Result { + let path = pathbuf.as_path(); + if path.is_file() { + return match ed25519::load_keypair_from_file(&pathbuf) { + Ok(k) => Ok(SigningConfig::Ed25519(k)), + Err(e) => Err(Box::new(e)), + }; + } else if path.is_dir() { + return Ok(SigningConfig::Tpm2( + Tpm2SigningConfig::load(&pathbuf)?, + )); + } else { + return Err(Box::new(io::Error::new( + io::ErrorKind::Other, + "Key needs to be a file or directory", + ))); + } + } + + pub fn public_key(self: &Self) -> Result, DynError> { + return Ok(match self { + SigningConfig::Ed25519(s) => { + s.verifying_key().to_bytes().to_vec() + } + SigningConfig::Tpm2(t) => t.public_key()?, + }); + } + + pub fn fingerprint(self: &Self) -> Result { + return Ok(match self { + SigningConfig::Ed25519(s) => { + ed25519::format_verifying_key( + &s.verifying_key(), + ) + } + SigningConfig::Tpm2(t) => t.fingerprint()?, + }); + } + + pub fn public_key_type(self: &Self) -> PublicKeyType { + return match self { + SigningConfig::Ed25519(_) => PublicKeyType::Ed25519, + SigningConfig::Tpm2(_) => PublicKeyType::Prime256v1, + }; + } + + pub fn sign( + self: &mut Self, + message: &[u8], + ) -> Result, DynError> { + return Ok(match self { + SigningConfig::Ed25519(s) => ed25519::sign(s, message), + SigningConfig::Tpm2(t) => t.sign(message)?, + }); + } +} + +#[cfg(test)] +mod test { + use crate::ed25519; + use crate::error::DynError; + use crate::fs; + use crate::test_common::verify_p256; + + #[test] + fn signing_config_tpm2() -> Result<(), DynError> { + let dir = fs::to_path_buf("tests/files/sign/tpm2"); + let pubkey = vec![ + 4, 217, 21, 3, 90, 153, 86, 215, 109, 144, 192, 156, + 64, 17, 161, 130, 133, 168, 173, 84, 110, 163, 117, 16, + 13, 6, 189, 149, 76, 182, 117, 240, 3, 169, 114, 51, + 120, 50, 218, 26, 145, 195, 103, 201, 172, 74, 97, 252, + 241, 179, 72, 7, 207, 179, 22, 70, 170, 238, 58, 81, + 102, 16, 237, 122, 128, + ]; + let mut sign_config = super::SigningConfig::load(&dir)?; + assert_eq!(sign_config.public_key()?, pubkey); + assert_eq!( + sign_config.fingerprint()?, + "7A:74:DE:CA:EB:62:76:8D\n\ + 60:50:68:8B:85:B9:5B:3D\n\ + 8E:C2:75:9F:BE:2B:FE:30\n\ + A9:45:2D:93:C6:9A:68:7E" + ); + assert_eq!( + sign_config.public_key_type(), + super::PublicKeyType::Prime256v1 + ); + let msg = b"ADifferentTestMessage".to_vec(); + let sig = sign_config.sign(&msg)?; + verify_p256(&pubkey, &msg, &sig)?; + return Ok(()); + } + + #[test] + fn signing_config_ed25519() -> Result<(), DynError> { + let file = fs::to_path_buf("tests/files/sign/ed25519"); + let pubkey = vec![ + 94, 183, 62, 28, 74, 112, 186, 74, 57, 152, 75, 149, + 127, 150, 26, 109, 4, 4, 7, 127, 72, 77, 143, 129, 183, + 228, 156, 146, 81, 210, 25, 249, + ]; + let mut sign_config = super::SigningConfig::load(&file)?; + assert_eq!(sign_config.public_key()?, pubkey); + assert_eq!( + sign_config.fingerprint()?, + "5E:B7:3E:1C:4A:70:BA:4A\n\ + 39:98:4B:95:7F:96:1A:6D\n\ + 04:04:07:7F:48:4D:8F:81\n\ + B7:E4:9C:92:51:D2:19:F9" + ); + assert_eq!( + sign_config.public_key_type(), + super::PublicKeyType::Ed25519, + ); + let msg = b"AnotherDifferentTestMessage".to_vec(); + let sig = sign_config.sign(&msg)?; + ed25519::verify(&pubkey, &msg, &sig)?; + return Ok(()); + } +} diff --git a/src/test_common.rs b/src/test_common.rs new file mode 100644 index 0000000..15648b4 --- /dev/null +++ b/src/test_common.rs @@ -0,0 +1,14 @@ +use crate::error::DynError; +use ed25519_dalek::Verifier; +use p256::ecdsa::{Signature, VerifyingKey}; + +pub fn verify_p256( + public_key: &Vec, + message: &Vec, + signature: &Vec, +) -> Result<(), DynError> { + let sig = Signature::from_der(&signature)?; + let verifying_key = VerifyingKey::from_sec1_bytes(&public_key)?; + verifying_key.verify(&message, &sig)?; + return Ok(()); +} diff --git a/src/tpm2.rs b/src/tpm2.rs deleted file mode 100644 index 7082153..0000000 --- a/src/tpm2.rs +++ /dev/null @@ -1,140 +0,0 @@ -use std::io; -use std::path::PathBuf; - -use crate::conv; -use crate::fs; -use crate::message; -use message::cryptographic_id::PublicKeyType; -use prost::Message; - -fn id_path(out: &PathBuf) -> PathBuf { - let mut out_id = out.clone(); - out_id.push("id.bin"); - return out_id; -} - -fn to_sign_path(out: &PathBuf) -> PathBuf { - let mut out_sig = out.clone(); - out_sig.push("to_sign.bin"); - return out_sig; -} - -pub fn build( - key_path: &PathBuf, - msg_path: &PathBuf, - timestamp: u64, - out: &PathBuf, -) -> io::Result<()> { - let msg = message::CryptographicId { - public_key: fs::read_file(&key_path)?, - timestamp: timestamp, - msg: fs::read_file(&msg_path)?, - signature: Vec::new(), - public_key_type: PublicKeyType::Prime256v1 as i32, - personal_information: Vec::new(), - }; - let out_id = id_path(&out); - fs::write_file(&message::to_data(&msg)?, &out_id)?; - let out_sign = to_sign_path(&out); - let sign_data = conv::flatten_binary_vec(&message::to_sign_arr(&msg)); - fs::write_file(&sign_data, &out_sign)?; - return Ok(()); -} - -pub fn read_id( - sig_path: &PathBuf, - out: &PathBuf, -) -> io::Result { - let id_path = id_path(&out); - let data = fs::read_file(&id_path)?; - let sig = fs::read_file(&sig_path)?; - let buf: &[u8] = &data; - let mut msg = message::CryptographicId::decode(buf)?; - msg.signature = sig; - return Ok(msg); -} - -#[cfg(test)] -mod tests { - use std::io; - - use super::fs; - use tempfile; - - #[test] - fn id_path() { - let path = fs::to_path_buf("/my/dir/to"); - assert_eq!( - super::id_path(&path), - fs::to_path_buf("/my/dir/to/id.bin") - ); - } - - #[test] - fn to_sign_path() { - let path = super::fs::to_path_buf("/my/important/dir"); - assert_eq!( - super::to_sign_path(&path), - fs::to_path_buf("/my/important/dir/to_sign.bin") - ); - } - - #[test] - fn build() -> io::Result<()> { - let tmpdir_in = tempfile::tempdir()?; - let tmpdir_out = tempfile::tempdir()?; - let key_path = tmpdir_in.path().join("keyfile.bin"); - let msg_path = tmpdir_in.path().join("msgfile.bin"); - let pubkey = b"a_wrong_pubkey".to_vec(); - let msg = b"my_message".to_vec(); - fs::write_file(&pubkey, &key_path)?; - fs::write_file(&msg, &msg_path)?; - let timestamp = 1412424562; - let outpath = &tmpdir_out.path().to_path_buf(); - super::build(&key_path, &msg_path, timestamp, &outpath)?; - let tosign = super::to_sign_path(&outpath); - assert_eq!( - fs::read_file(&tosign)?, - [ - 0, 0, 0, 0, 84, 47, 227, 114, 97, 95, 119, 114, - 111, 110, 103, 95, 112, 117, 98, 107, 101, 121, - 109, 121, 95, 109, 101, 115, 115, 97, 103, 101 - ] - ); - let sig_path = tmpdir_in.path().join("sig.bin"); - let sig = b"a broken sig".to_vec(); - fs::write_file(&sig, &sig_path)?; - let id = super::read_id(&sig_path, &outpath)?; - let compare_id = super::message::CryptographicId { - public_key: pubkey, - timestamp: timestamp, - msg: msg, - signature: sig, - public_key_type: super::PublicKeyType::Prime256v1 - as i32, - personal_information: Vec::new(), - }; - assert_eq!(id, compare_id); - return Ok(()); - } - - #[test] - fn read_id() { - let out_path = fs::to_path_buf("tests/files/tpm2/read_id"); - let sig_path = - fs::to_path_buf("tests/files/tpm2/read_id/sig.bin"); - let id = super::read_id(&sig_path, &out_path).unwrap(); - let compare_id = super::message::CryptographicId { - public_key: vec![7, 46, 24, 93, 146, 72, 214, 162], - timestamp: 1687083430, - msg: vec![ - 87, 114, 105, 116, 101, 32, 109, 101, 32, 97, - 110, 32, 101, 109, 97, 105, 108, 33, - ], - signature: vec![72, 83, 24, 92, 251, 82, 184, 83], - public_key_type: super::PublicKeyType::Ed25519 as i32, - personal_information: Vec::new(), - }; - assert_eq!(id, compare_id); - } -} diff --git a/src/tpm2/common.rs b/src/tpm2/common.rs new file mode 100644 index 0000000..5d79372 --- /dev/null +++ b/src/tpm2/common.rs @@ -0,0 +1,429 @@ +use crate::conv; +use crate::error::DynError; +use sha2::Digest as Sha2Digest; +use std::path::PathBuf; +pub use tss_esapi::Error; +use tss_esapi::{ + attributes::ObjectAttributesBuilder, + constants::SessionType, + interface_types::{ + algorithm::{HashingAlgorithm, PublicAlgorithm, SymmetricMode}, + ecc::EccCurve, + key_bits::AesKeyBits, + resource_handles::Hierarchy, + session_handles::AuthSession, + }, + structures::{ + CreatePrimaryKeyResult, Digest, EccPoint, EccScheme, + KeyDerivationFunctionScheme, MaxBuffer, + PcrSelectionListBuilder, PcrSlot, Public, PublicBuilder, + PublicEccParametersBuilder, SymmetricDefinition, + SymmetricDefinitionObject, + }, + Context, TctiNameConf, WrapperErrorKind, +}; + +pub fn to_private_file(dir: &PathBuf) -> PathBuf { + let mut p = dir.clone(); + p.push("ecc.priv"); + return p; +} + +pub fn to_public_file(dir: &PathBuf) -> PathBuf { + let mut p = dir.clone(); + p.push("ecc.pub"); + return p; +} + +pub fn to_pcr_file(dir: &PathBuf) -> PathBuf { + let mut p = dir.clone(); + p.push("pcrs"); + return p; +} + +pub fn to_handle_file(dir: &PathBuf) -> PathBuf { + let mut p = dir.clone(); + p.push("handle"); + return p; +} + +pub fn str_to_pcr(s: &str) -> Result { + let num: u32 = s.parse()?; + return Ok(PcrSlot::try_from(1 << num)?); +} + +pub fn create_context() -> Result { + let device = TctiNameConf::from_environment_variable()?; + return Ok(Context::new(device)?); +} + +pub fn start_auth_session(context: &mut Context) -> Result { + let session = context.start_auth_session( + None, + None, + None, + SessionType::Policy, + SymmetricDefinition::AES_256_CFB, + HashingAlgorithm::Sha256, + )?; + return match session { + Some(s) => Ok(s), + None => Err(Error::WrapperError( + WrapperErrorKind::WrongValueFromTpm, + )), + }; +} + +pub fn create_public_for_primary(policy: Digest) -> Result { + let ecc_params = PublicEccParametersBuilder::new() + .with_ecc_scheme(EccScheme::Null) + .with_curve(EccCurve::NistP256) + .with_is_decryption_key(true) + .with_restricted(true) + .with_symmetric(SymmetricDefinitionObject::Aes { + key_bits: AesKeyBits::Aes128, + mode: SymmetricMode::Cfb, + }) + .with_key_derivation_function_scheme( + KeyDerivationFunctionScheme::Null, + ) + .build()?; + let primary_object_attributes = ObjectAttributesBuilder::new() + .with_fixed_tpm(true) + .with_fixed_parent(true) + .with_sensitive_data_origin(true) + // TODO remove with_user_with_auth in a v2 + // the key is already protected by the policy, this will also + // protect the primary key with it. + .with_user_with_auth(true) + .with_restricted(true) + .with_decrypt(true) + .build()?; + let primary_public = PublicBuilder::new() + .with_public_algorithm(PublicAlgorithm::Ecc) + .with_name_hashing_algorithm(HashingAlgorithm::Sha256) + .with_object_attributes(primary_object_attributes) + .with_ecc_parameters(ecc_params) + .with_ecc_unique_identifier(EccPoint::default()) + .with_auth_policy(policy) + .build()?; + return Ok(primary_public); +} + +pub fn create_primary( + context: &mut Context, + policy: Digest, +) -> Result { + let primary_public = create_public_for_primary(policy)?; + return context.execute_with_nullauth_session(|ctx| { + ctx.create_primary( + Hierarchy::Endorsement, + primary_public.clone(), + None, + None, + None, + None, + ) + }); +} + +pub fn set_policy( + context: &mut Context, + pcrs: &Vec, + session: AuthSession, +) -> Result<(), DynError> { + let pcr_selection_list = PcrSelectionListBuilder::new() + .with_selection(HashingAlgorithm::Sha256, pcrs.as_slice()) + .build()?; + + let (_update_counter, pcr_sel, pcr_data) = context + .execute_without_session(|ctx| { + ctx.pcr_read(pcr_selection_list) + })?; + let concatenated_pcr_values = pcr_data + .value() + .iter() + .map(|x| x.as_bytes()) + .collect::>() + .concat(); + let concatenated_pcr_values = + MaxBuffer::try_from(concatenated_pcr_values)?; + let (hashed_data, _ticket) = + context.execute_without_session(|ctx| { + ctx.hash( + concatenated_pcr_values, + HashingAlgorithm::Sha256, + Hierarchy::Endorsement, + ) + })?; + context.policy_pcr( + session.try_into()?, + hashed_data.clone(), + pcr_sel.clone(), + )?; + return Ok(()); +} + +pub fn public_key(public: &Public) -> Result, DynError> { + let Public::Ecc { unique, .. } = public else { + return Err(Box::new(Error::WrapperError( + WrapperErrorKind::InvalidParam, + ))); + }; + let uncompressed: u8 = 4; + let key = conv::flatten_binary_vec(&vec![ + vec![uncompressed], + unique.x().as_bytes().to_vec(), + unique.y().as_bytes().to_vec(), + ]); + return Ok(key); +} + +pub fn format_public_key(public: &Public) -> Result { + let key = public_key(&public)?; + let mut hasher = sha2::Sha256::new(); + hasher.update(&key[1..]); + let bytes_vec = hasher.finalize(); + let hex = conv::bytes_to_hex(bytes_vec.to_vec()); + return Ok(vec![ + hex[0..23].to_string(), + hex[24..47].to_string(), + hex[48..71].to_string(), + hex[72..95].to_string(), + ] + .join("\n")); +} + +#[cfg(test)] +mod tests { + use crate::error::DynError; + use crate::fs; + use crate::tpm2; + use tss_esapi::{ + constants::{SessionType, StructureTag::Creation}, + interface_types::{ + resource_handles::Hierarchy, + session_handles::PolicySession, + }, + structures::{Digest, Ticket}, + traits::Marshall, + }; + + #[test] + fn to_private_file() { + let path = fs::to_path_buf("/my/dir/to"); + assert_eq!( + super::to_private_file(&path), + fs::to_path_buf("/my/dir/to/ecc.priv") + ); + } + + #[test] + fn to_public_file() { + let path = fs::to_path_buf("/my/dir/to"); + assert_eq!( + super::to_public_file(&path), + fs::to_path_buf("/my/dir/to/ecc.pub") + ); + } + + #[test] + fn to_pcr_file() { + let path = fs::to_path_buf("/my/dir/to"); + assert_eq!( + super::to_pcr_file(&path), + fs::to_path_buf("/my/dir/to/pcrs") + ); + } + + #[test] + fn to_handle_file() { + let path = fs::to_path_buf("/my/dir/to"); + assert_eq!( + super::to_handle_file(&path), + fs::to_path_buf("/my/dir/to/handle") + ); + } + + #[test] + fn str_to_pcr() -> Result<(), DynError> { + assert_eq!(super::str_to_pcr("4")?, super::PcrSlot::Slot4); + assert_eq!(super::str_to_pcr("6")?, super::PcrSlot::Slot6); + assert_eq!(super::str_to_pcr("7")?, super::PcrSlot::Slot7); + assert_eq!(super::str_to_pcr("14")?, super::PcrSlot::Slot14); + return Ok(()); + } + + #[test] + fn create_context() -> Result<(), DynError> { + let mut context = super::create_context()?; + // test it can be used + super::start_auth_session(&mut context)?; + return Ok(()); + } + + #[test] + fn start_auth_session() -> Result<(), DynError> { + let mut context = super::create_context()?; + let session = super::start_auth_session(&mut context)?; + let super::AuthSession::PolicySession(policy) = session else { + return Err("Wrong AuthSession".into()); + }; + match policy { + PolicySession::PolicySession { + hashing_algorithm: h, + session_handle: _, + session_type: t, + } => { + assert_eq!(h, super::HashingAlgorithm::Sha256); + assert_eq!(t, SessionType::Policy); + } + } + return Ok(()); + } + + #[test] + fn create_public_for_primary() -> Result<(), DynError> { + let digest = Digest::try_from([ + 139, 67, 252, 206, 35, 178, 254, 86, 130, 216, 27, 41, + 104, 74, 93, 8, 215, 146, 120, 21, 6, 17, 220, 126, 89, + 9, 87, 123, 64, 19, 10, 139, + ])?; + let public = super::create_public_for_primary(digest)?; + assert_eq!( + public.marshall()?, + [ + 0, 35, 0, 11, 0, 3, 0, 114, 0, 32, 139, 67, + 252, 206, 35, 178, 254, 86, 130, 216, 27, 41, + 104, 74, 93, 8, 215, 146, 120, 21, 6, 17, 220, + 126, 89, 9, 87, 123, 64, 19, 10, 139, 0, 6, 0, + 128, 0, 67, 0, 16, 0, 3, 0, 16, 0, 0, 0, 0 + ] + ); + return Ok(()); + } + + #[test] + fn create_primary() -> Result<(), DynError> { + let mut context = super::create_context()?; + let digest = Digest::try_from([ + 104, 74, 93, 8, 215, 146, 120, 21, 6, 17, 220, 126, 89, + 139, 67, 52, 206, 35, 18, 254, 86, 130, 216, 27, 41, 9, + 87, 123, 64, 19, 10, 139, + ])?; + let res = super::create_primary(&mut context, digest)?; + assert_eq!( + res.out_public.marshall()?, + [ + 0, 35, 0, 11, 0, 3, 0, 114, 0, 32, 104, 74, 93, + 8, 215, 146, 120, 21, 6, 17, 220, 126, 89, 139, + 67, 52, 206, 35, 18, 254, 86, 130, 216, 27, 41, + 9, 87, 123, 64, 19, 10, 139, 0, 6, 0, 128, 0, + 67, 0, 16, 0, 3, 0, 16, 0, 32, 105, 12, 58, + 253, 147, 216, 247, 79, 148, 3, 44, 208, 244, + 192, 186, 234, 219, 53, 56, 84, 188, 205, 205, + 22, 147, 159, 235, 53, 236, 249, 111, 124, 0, + 32, 31, 210, 253, 103, 241, 70, 178, 220, 114, + 202, 16, 210, 251, 241, 36, 71, 240, 1, 9, 59, + 51, 61, 100, 213, 139, 98, 160, 149, 171, 215, + 207, 129 + ] + ); + assert_eq!( + res.creation_hash, + Digest::try_from([ + 40, 208, 38, 250, 253, 116, 145, 6, 116, 62, + 39, 196, 40, 5, 81, 88, 94, 93, 23, 102, 142, + 181, 33, 131, 94, 214, 1, 39, 239, 252, 5, 212 + ])? + ); + + assert_eq!(res.creation_ticket.tag(), Creation.into()); + assert_eq!( + res.creation_ticket.hierarchy(), + Hierarchy::Endorsement + ); + assert_eq!( + res.creation_ticket.digest(), + [ + 105, 20, 22, 135, 177, 254, 247, 189, 215, 111, + 187, 249, 34, 106, 163, 35, 15, 253, 167, 88, + 209, 72, 18, 5, 252, 114, 160, 193, 99, 229, + 174, 162, 114, 83, 7, 227, 173, 69, 165, 111, + 74, 245, 44, 61, 104, 154, 58, 18, 139, 221, + 37, 83, 62, 109, 139, 249, 123, 135, 137, 18, + 102, 10, 22, 85 + ] + ); + return Ok(()); + } + + #[test] + fn set_policy() -> Result<(), DynError> { + let mut context = super::create_context()?; + let session = super::start_auth_session(&mut context)?; + let pcrs = vec![super::PcrSlot::Slot4, super::PcrSlot::Slot7]; + super::set_policy(&mut context, &pcrs, session)?; + let session_digest = + context.policy_get_digest(session.try_into()?)?; + assert_eq!( + session_digest, + Digest::try_from([ + 244, 176, 153, 60, 161, 122, 79, 97, 227, 67, + 167, 86, 204, 57, 187, 62, 229, 184, 18, 180, + 79, 42, 235, 172, 74, 189, 195, 190, 237, 177, + 202, 65 + ])? + ); + + context.policy_restart(session.try_into()?)?; + super::set_policy(&mut context, &vec![], session)?; + let session_digest2 = + context.policy_get_digest(session.try_into()?)?; + assert_eq!( + session_digest2, + Digest::try_from([ + 125, 240, 82, 243, 104, 54, 228, 33, 118, 170, + 160, 184, 0, 165, 110, 243, 94, 215, 40, 244, + 108, 108, 180, 204, 131, 213, 96, 89, 73, 11, + 163, 97 + ])? + ); + return Ok(()); + } + + #[test] + fn public_key() -> Result<(), DynError> { + let public = tpm2::read_public_file(&fs::to_path_buf( + "tests/files/tpm2/common/public", + ))?; + assert_eq!( + super::public_key(&public)?, + vec![ + 4, 4, 184, 250, 128, 44, 227, 198, 40, 4, 13, + 255, 100, 208, 40, 139, 26, 77, 51, 5, 196, + 245, 215, 14, 43, 193, 154, 79, 84, 35, 95, 45, + 46, 68, 53, 131, 217, 26, 43, 11, 114, 75, 128, + 75, 238, 184, 205, 132, 170, 167, 213, 26, 75, + 140, 87, 135, 88, 130, 248, 206, 4, 234, 53, + 158, 200 + ] + ); + return Ok(()); + } + + #[test] + fn format_public_key() -> Result<(), DynError> { + let public = tpm2::read_public_file(&fs::to_path_buf( + "tests/files/tpm2/common/public", + ))?; + assert_eq!( + super::format_public_key(&public)?, + "EA:E2:A2:E4:A4:0E:86:8E\n\ + 23:60:CB:DC:02:BB:7B:EB\n\ + 42:87:25:87:1D:8C:40:EF\n\ + 73:80:F1:74:3A:AC:45:C9" + ); + return Ok(()); + } +} diff --git a/src/tpm2/mod.rs b/src/tpm2/mod.rs new file mode 100644 index 0000000..3c5d049 --- /dev/null +++ b/src/tpm2/mod.rs @@ -0,0 +1,5 @@ +mod common; +mod sign; + +pub use common::*; +pub use sign::*; diff --git a/src/tpm2/sign.rs b/src/tpm2/sign.rs new file mode 100644 index 0000000..a937671 --- /dev/null +++ b/src/tpm2/sign.rs @@ -0,0 +1,400 @@ +use asn1_rs::ToDer; +use std::path::PathBuf; +pub use tss_esapi::Error; +use tss_esapi::{ + handles::KeyHandle, + interface_types::{ + algorithm::HashingAlgorithm, resource_handles::Hierarchy, + session_handles::AuthSession, + }, + structures::{ + Auth, CreatePrimaryKeyResult, Digest, EccSignature, HashScheme, + MaxBuffer, PcrSlot, Private, Public, PublicBuffer, Signature, + SignatureScheme, + }, + traits::UnMarshall, + Context, WrapperErrorKind, +}; + +use crate::error::DynError; +use crate::fs; +use crate::tpm2; + +fn signature_to_asn1(sig: &EccSignature) -> Result, DynError> { + let mut v = Vec::new(); + let r_arr: [u8; 32] = sig.signature_r().as_bytes().try_into()?; + let s_arr: [u8; 32] = sig.signature_s().as_bytes().try_into()?; + let rb = asn1_rs::Integer::from_const_array(r_arr); + let sb = asn1_rs::Integer::from_const_array(s_arr); + rb.write_der(&mut v)?; + sb.write_der(&mut v)?; + return Ok(asn1_rs::Sequence::new(v.into()).to_der_vec()?); +} + +pub fn read_public_file(dir: &PathBuf) -> Result { + let public_content = fs::read_file(&tpm2::to_public_file(&dir))?; + let public_buf = PublicBuffer::unmarshall(&public_content)?; + let public = Public::unmarshall(&public_buf)?; + return Ok(public); +} + +fn read_private_file(dir: &PathBuf) -> Result { + let private_content = fs::read_file(&tpm2::to_private_file(&dir))?; + let private = Private::unmarshall(&private_content)?; + return Ok(private); +} + +fn read_pcr_file(dir: &PathBuf) -> Result, DynError> { + let mut result = vec![]; + let binary = match fs::read_file(&tpm2::to_pcr_file(&dir)) { + Ok(s) => s, + Err(_) => { + // No file means no pcrs + return Ok(result); + } + }; + let content = std::str::from_utf8(&binary)?; + let cleared = content.replace(" ", "").replace("\n", ""); + let parts = cleared.split(","); + for part in parts { + result.push(tpm2::str_to_pcr(part)?); + } + return Ok(result); +} + +pub struct Tpm2SigningConfig { + context: Context, + key_handle: KeyHandle, + public: Public, + session: AuthSession, + pcrs: Vec, +} + +fn _load_public_and_handle( + context: &mut Context, + primary: &CreatePrimaryKeyResult, + pathbuf: &PathBuf, +) -> Result<(Public, KeyHandle), DynError> { + let handle_file = tpm2::to_handle_file(&pathbuf); + if handle_file.exists() { + let content = fs::read_file(&tpm2::to_handle_file(&pathbuf))?; + let key_handle: KeyHandle = + context.tr_deserialize(&content)?.into(); + let (public, _, _) = context.read_public(key_handle)?; + return Ok((public, key_handle)); + } else { + let public = read_public_file(&pathbuf)?; + let private = read_private_file(&pathbuf)?; + let key_handle = + context.execute_with_nullauth_session(|ctx| { + ctx.load( + primary.key_handle, + private, + public.clone(), + ) + })?; + return Ok((public, key_handle)); + } +} + +impl Tpm2SigningConfig { + pub fn load(pathbuf: &PathBuf) -> Result { + let mut context = tpm2::create_context()?; + let pcrs = read_pcr_file(&pathbuf)?; + let session_digest = if pcrs.len() == 0 { + Digest::try_from(vec![])? + } else { + let session = tpm2::start_auth_session(&mut context)?; + tpm2::set_policy(&mut context, &pcrs, session)?; + context.policy_get_digest(session.try_into()?)? + }; + let primary = tpm2::create_primary( + &mut context, + session_digest.clone(), + )?; + let (public, key_handle) = _load_public_and_handle( + &mut context, + &primary, + &pathbuf, + )?; + let save_session = if pcrs.len() == 0 { + context.tr_set_auth( + key_handle.into(), + Auth::try_from(b"x".to_vec())?, + )?; + AuthSession::Password + } else { + tpm2::start_auth_session(&mut context)? + }; + return Ok(Self { + context: context, + key_handle: key_handle, + public: public, + session: save_session, + pcrs: pcrs, + }); + } + + pub fn public_key(self: &Self) -> Result, DynError> { + return tpm2::public_key(&self.public); + } + + pub fn fingerprint(self: &Self) -> Result { + return tpm2::format_public_key(&self.public); + } + + pub fn sign( + self: &mut Self, + message: &[u8], + ) -> Result, DynError> { + if self.pcrs.len() > 0 { + // otherwise TPM2 fails to sign on multiple runs + self.context + .policy_restart(self.session.try_into()?)?; + tpm2::set_policy( + &mut self.context, + &self.pcrs, + self.session.try_into()?, + )?; + } + let data = MaxBuffer::try_from(message.to_vec())?; + let (digest, ticket) = + self.context.execute_without_session(|ctx| { + ctx.hash( + data, + HashingAlgorithm::Sha256, + Hierarchy::Endorsement, + ) + })?; + let sig_scheme = SignatureScheme::EcDsa { + scheme: HashScheme::new(HashingAlgorithm::Sha256), + }; + let signature = self.context.execute_with_session( + Some(self.session), + |ctx| { + ctx.sign( + self.key_handle, + digest, + sig_scheme, + ticket, + ) + }, + )?; + if let Signature::EcDsa(ecc_sig) = signature { + return Ok(signature_to_asn1(&ecc_sig)?); + } else { + return Err(Box::new(Error::WrapperError( + WrapperErrorKind::InvalidParam, + ))); + } + } +} + +#[cfg(test)] +mod test { + use super::PcrSlot; + use crate::test_common::verify_p256; + use crate::tpm2::sign::DynError; + use std::path::PathBuf; + use tss_esapi::{ + structures::{EccParameter, Public}, + traits::UnMarshall, + }; + + #[test] + fn signature_to_asn1() -> Result<(), DynError> { + let s = vec![ + 225, 46, 176, 134, 227, 104, 125, 27, 233, 222, 205, + 246, 131, 31, 17, 119, 74, 241, 74, 161, 57, 2, 194, + 124, 110, 196, 15, 44, 113, 118, 214, 73, + ]; + let r = vec![ + 172, 188, 189, 20, 83, 126, 42, 89, 226, 6, 137, 195, + 49, 251, 210, 70, 46, 64, 64, 240, 54, 12, 210, 161, + 37, 216, 91, 70, 100, 18, 210, 213, + ]; + let sig = super::EccSignature::create( + super::HashingAlgorithm::Sha256, + EccParameter::try_from(s.clone())?, + EccParameter::try_from(r.clone())?, + )?; + let sequence = vec![48, 70]; + let int_start = vec![2, 33, 0]; + assert_eq!( + super::signature_to_asn1(&sig)?, + [ + sequence.clone(), + int_start.clone(), + s, + int_start.clone(), + r + ] + .concat() + ); + return Ok(()); + } + + #[test] + fn read_public_file() -> Result<(), DynError> { + let public = super::read_public_file(&super::fs::to_path_buf( + "tests/files/tpm2/sign/read_public_file", + ))?; + let expected = Public::unmarshall(&vec![ + 0, 35, 0, 11, 0, 4, 4, 114, 0, 0, 0, 16, 0, 16, 0, 3, + 0, 16, 0, 32, 180, 200, 124, 255, 187, 37, 107, 72, + 152, 46, 160, 146, 84, 240, 231, 138, 162, 204, 75, 93, + 222, 94, 219, 157, 8, 105, 100, 149, 177, 19, 183, 72, + 0, 32, 110, 210, 241, 200, 15, 150, 192, 118, 71, 0, + 241, 170, 65, 245, 134, 67, 152, 216, 82, 31, 114, 104, + 170, 79, 244, 183, 76, 247, 85, 63, 145, 160, + ])?; + assert_eq!(public, expected); + return Ok(()); + } + + #[test] + fn read_private_file() -> Result<(), DynError> { + let private = + super::read_private_file(&super::fs::to_path_buf( + "tests/files/tpm2/sign/read_private_file", + ))?; + let cmp = super::Private::try_from(vec![ + 0, 32, 227, 140, 85, 248, 217, 55, 161, 174, 38, 115, + 230, 217, 197, 164, 102, 165, 57, 69, 174, 184, 76, + 100, 210, 254, 111, 46, 136, 127, 45, 24, 60, 186, 0, + 16, 27, 3, 223, 189, 173, 114, 126, 51, 98, 56, 69, 78, + 229, 220, 29, 54, 163, 18, 102, 152, 115, 22, 113, 155, + 180, 255, 207, 237, 235, 165, 88, 151, 51, 109, 103, + 84, 93, 108, 101, 82, 222, 165, 160, 188, 106, 10, 89, + 26, 189, 9, 153, 130, 114, 13, 217, 169, 113, 98, 205, + 75, 78, 181, 66, 178, 114, 112, 201, 86, 103, 3, 202, + 60, 163, 127, 25, 113, 74, 14, 0, 175, 43, 54, 144, + 238, 33, 11, 29, 247, 8, 216, + ])?; + assert_eq!(private, cmp); + return Ok(()); + } + + #[test] + fn read_pcr_file() -> Result<(), DynError> { + assert_eq!( + super::read_pcr_file(&super::fs::to_path_buf( + "/my/dir/no/pcrs" + ))?, + vec![] + ); + assert_eq!( + super::read_pcr_file(&super::fs::to_path_buf( + "tests/files/tpm2/sign/read_pcr_file/seven" + ))?, + vec![PcrSlot::Slot7] + ); + assert_eq!( + super::read_pcr_file(&super::fs::to_path_buf( + "tests/files/tpm2/sign/read_pcr_file/multiple" + ))?, + vec![PcrSlot::Slot4, PcrSlot::Slot7, PcrSlot::Slot14] + ); + let error = super::read_pcr_file(&super::fs::to_path_buf( + "tests/files/tpm2/sign/read_pcr_file/empty", + )) + .unwrap_err(); + assert_eq!( + format!("{:?}", error), + "ParseIntError { kind: Empty }" + ); + return Ok(()); + } + + fn test_sign(dir: &PathBuf, pubkey: &Vec) -> Result<(), DynError> { + let mut sign_config = super::Tpm2SigningConfig::load(&dir)?; + assert_eq!(sign_config.public_key()?, *pubkey); + // Test signing works multiple times + for _ in 0..5 { + let message = b"testmessage".to_vec(); + let signature = sign_config.sign(&message)?; + verify_p256(&pubkey, &message, &signature)?; + } + return Ok(()); + } + + #[test] + fn tpm2_signing_config_plain() -> Result<(), DynError> { + let dir = super::fs::to_path_buf( + "tests/files/tpm2/sign/sign/plain", + ); + let pubkey = vec![ + 4, 42, 12, 170, 201, 52, 112, 214, 207, 144, 58, 167, + 96, 26, 140, 36, 229, 135, 23, 129, 235, 204, 109, 5, + 223, 117, 119, 82, 155, 95, 192, 30, 145, 128, 78, 144, + 192, 142, 163, 129, 193, 250, 32, 88, 111, 212, 170, + 246, 216, 86, 167, 174, 203, 236, 181, 164, 230, 193, + 84, 41, 136, 163, 203, 53, 122, + ]; + return test_sign(&dir, &pubkey); + } + + #[test] + fn tpm2_signing_config_pcr() -> Result<(), DynError> { + let dir = super::fs::to_path_buf( + "tests/files/tpm2/sign/sign/pcr_4_7", + ); + let pubkey = vec![ + 4, 217, 21, 3, 90, 153, 86, 215, 109, 144, 192, 156, + 64, 17, 161, 130, 133, 168, 173, 84, 110, 163, 117, 16, + 13, 6, 189, 149, 76, 182, 117, 240, 3, 169, 114, 51, + 120, 50, 218, 26, 145, 195, 103, 201, 172, 74, 97, 252, + 241, 179, 72, 7, 207, 179, 22, 70, 170, 238, 58, 81, + 102, 16, 237, 122, 128, + ]; + return test_sign(&dir, &pubkey); + } + + #[test] + fn tpm2_signing_config_handle() -> Result<(), DynError> { + let dir = super::fs::to_path_buf( + "tests/files/tpm2/sign/sign/handle", + ); + let pubkey = vec![ + 4, 88, 108, 226, 103, 251, 196, 213, 2, 237, 184, 190, + 201, 76, 85, 239, 241, 221, 192, 57, 229, 1, 74, 197, + 214, 156, 214, 238, 101, 177, 72, 63, 143, 87, 35, 95, + 211, 53, 219, 167, 132, 193, 128, 183, 7, 109, 184, + 103, 62, 66, 142, 149, 148, 25, 210, 24, 248, 146, 173, + 134, 155, 145, 211, 20, 133, + ]; + return test_sign(&dir, &pubkey); + } + + #[test] + fn tpm2_signing_config_handle_pcr() -> Result<(), DynError> { + let dir = super::fs::to_path_buf( + "tests/files/tpm2/sign/sign/handle_pcr_5_6_7", + ); + let pubkey = vec![ + 4, 119, 138, 76, 158, 24, 53, 109, 38, 216, 131, 18, + 19, 135, 249, 97, 141, 17, 208, 70, 120, 160, 254, 35, + 129, 130, 2, 207, 146, 149, 122, 81, 240, 165, 1, 242, + 135, 237, 35, 74, 75, 254, 250, 86, 167, 134, 151, 201, + 255, 115, 168, 3, 16, 183, 155, 138, 39, 5, 81, 30, + 108, 236, 24, 174, 204, + ]; + return test_sign(&dir, &pubkey); + } + + #[test] + fn tpm2_signing_config_fingerprint() -> Result<(), DynError> { + let dir = super::fs::to_path_buf( + "tests/files/tpm2/sign/sign/handle_pcr_5_6_7", + ); + let sign_config = super::Tpm2SigningConfig::load(&dir)?; + assert_eq!( + sign_config.fingerprint()?, + "25:0E:F9:F7:0F:EA:47:19\n\ + 59:05:17:A6:85:1F:FE:10\n\ + 0B:DB:1F:2B:4F:AA:93:07\n\ + E8:43:02:BD:A5:F6:55:85" + ); + return Ok(()); + } +} diff --git a/tests/files/message/sign/key b/tests/files/message/sign/key_ed25519 similarity index 100% rename from tests/files/message/sign/key rename to tests/files/message/sign/key_ed25519 diff --git a/tests/files/message/sign/result b/tests/files/message/sign/result_ed25519 similarity index 100% rename from tests/files/message/sign/result rename to tests/files/message/sign/result_ed25519 diff --git a/tests/files/message/sign/tpm2/ecc.priv b/tests/files/message/sign/tpm2/ecc.priv new file mode 100644 index 0000000000000000000000000000000000000000..decea21e5dd567c8953de41630ed08edaf4e0812 GIT binary patch literal 128 zcmV-`0Du1gegGf^*_Mhf^ImAUiM;LRrEsJYJO2j|CkeN{%Iyr7!dwgG1i+4zl)59S3=VPgsnQ i#UGovQ(Y;T=RSM4Zp*nc<15h6o&Pa6#Yz literal 0 HcmV?d00001 diff --git a/tests/files/message/sign/tpm2/ecc.pub b/tests/files/message/sign/tpm2/ecc.pub new file mode 100644 index 0000000000000000000000000000000000000000..c0286031ad6cbb63583c26a6f0ef85e38fda3737 GIT binary patch literal 120 zcmV-;0Ehnob^s#)3jhQJG5{d-u$eredQV~FL#I~EIlDgPxDvEaD(kFDy~DokvC2UJ z5C9MW0{{>JAlVfITA5bYZIHm6KoOyWg{ZAmZliS&4FI#Fg2?RtRme=-07 literal 0 HcmV?d00001 diff --git a/tests/files/message/sign/tpm2/pcrs b/tests/files/message/sign/tpm2/pcrs new file mode 100644 index 0000000..f64ebbb --- /dev/null +++ b/tests/files/message/sign/tpm2/pcrs @@ -0,0 +1 @@ +4,7 \ No newline at end of file diff --git a/tests/files/message/sign/tpm2/public_key.der b/tests/files/message/sign/tpm2/public_key.der new file mode 100644 index 0000000000000000000000000000000000000000..059b054d501cd61dd3c51a4a3a79b37ee4f09730 GIT binary patch literal 91 zcmXqrG!SNE*J|@PXUoLM#sOw9GqN)~F|gbeWsaH|c0G5(fjJI>3!7S3tPROqTq?lJ uws)$}w$cyGD~pUPjBZIyJe+=VjaTBIkDEQ%&u&C]^>JpJ9KmHM䜒Q \ No newline at end of file diff --git a/tests/files/sign/tpm2/ecc.priv b/tests/files/sign/tpm2/ecc.priv new file mode 100644 index 0000000000000000000000000000000000000000..decea21e5dd567c8953de41630ed08edaf4e0812 GIT binary patch literal 128 zcmV-`0Du1gegGf^*_Mhf^ImAUiM;LRrEsJYJO2j|CkeN{%Iyr7!dwgG1i+4zl)59S3=VPgsnQ i#UGovQ(Y;T=RSM4Zp*nc<15h6o&Pa6#Yz literal 0 HcmV?d00001 diff --git a/tests/files/sign/tpm2/ecc.pub b/tests/files/sign/tpm2/ecc.pub new file mode 100644 index 0000000000000000000000000000000000000000..c0286031ad6cbb63583c26a6f0ef85e38fda3737 GIT binary patch literal 120 zcmV-;0Ehnob^s#)3jhQJG5{d-u$eredQV~FL#I~EIlDgPxDvEaD(kFDy~DokvC2UJ z5C9MW0{{>JAlVfITA5bYZIHm6KoOyWg{ZAmZliS&4FI#Fg2?RtRme=-07 literal 0 HcmV?d00001 diff --git a/tests/files/sign/tpm2/pcrs b/tests/files/sign/tpm2/pcrs new file mode 100644 index 0000000..f64ebbb --- /dev/null +++ b/tests/files/sign/tpm2/pcrs @@ -0,0 +1 @@ +4,7 \ No newline at end of file diff --git a/tests/files/sign/tpm2/public_key.der b/tests/files/sign/tpm2/public_key.der new file mode 100644 index 0000000000000000000000000000000000000000..059b054d501cd61dd3c51a4a3a79b37ee4f09730 GIT binary patch literal 91 zcmXqrG!SNE*J|@PXUoLM#sOw9GqN)~F|gbeWsaH|c0G5(fjJI>3!7S3tPROqTq?lJ uws)$}w$cyGD~pUPjBZIyJe+=VjaTBIkDEQ%&u iOHQ>yvT|_AR%ZjsJfnXZaY_yVuPZi??jZ{u_XyY|Vme{~ literal 0 HcmV?d00001 diff --git a/tests/files/tpm2/sign/read_public_file/ecc.pub b/tests/files/tpm2/sign/read_public_file/ecc.pub new file mode 100644 index 0000000000000000000000000000000000000000..821ea468975b09839bec81601bf05a97fed777de GIT binary patch literal 88 zcmZPwV^C(`W?*3{Vqjnp077OUR@ic)=KpThY>ydw3nqnpc;2<>jCbt4xZ86%GE=5* s6yEN^ppbXz;|cz02g=+TKCW{7+U7jtMv#0_#w!0W+kL)=+D}{n000UjyZ`_I literal 0 HcmV?d00001 diff --git a/tests/files/tpm2/sign/sign/handle/handle b/tests/files/tpm2/sign/sign/handle/handle new file mode 100644 index 0000000000000000000000000000000000000000..e0c23c9106726f59125dbc0b644b128a2713d041 GIT binary patch literal 132 zcmZo-X2P~g5dL6ws=i0l}jUM*>3<}}O k@s~|+FK;>6u$?`3N4lL;-_$9Rmn42nTH7{z;$@Lm0PBi0Hvj+t literal 0 HcmV?d00001 diff --git a/tests/files/tpm2/sign/sign/handle/public_key.der b/tests/files/tpm2/sign/sign/handle/public_key.der new file mode 100644 index 0000000000000000000000000000000000000000..a1968d5f73e15756608ebf94199422e7745fdd12 GIT binary patch literal 91 zcmXqrG!SNE*J|@PXUoLM#sOw9GqN)~F|b7BJWBt4 literal 0 HcmV?d00001 diff --git a/tests/files/tpm2/sign/sign/handle/public_key.pem b/tests/files/tpm2/sign/sign/handle/public_key.pem new file mode 100644 index 0000000..64d1e7c --- /dev/null +++ b/tests/files/tpm2/sign/sign/handle/public_key.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWGziZ/vE1QLtuL7JTFXv8d3AOeUB +SsXWnNbuZbFIP49XI1/TNdunhMGAtwdtuGc+Qo6VlBnSGPiSrYabkdMUhQ== +-----END PUBLIC KEY----- diff --git a/tests/files/tpm2/sign/sign/handle/public_key.raw b/tests/files/tpm2/sign/sign/handle/public_key.raw new file mode 100644 index 0000000..649909f --- /dev/null +++ b/tests/files/tpm2/sign/sign/handle/public_key.raw @@ -0,0 +1 @@ +XlgLU9J֜eH?W#_5ۧmg>B \ No newline at end of file diff --git a/tests/files/tpm2/sign/sign/handle_pcr_5_6_7/handle b/tests/files/tpm2/sign/sign/handle_pcr_5_6_7/handle new file mode 100644 index 0000000000000000000000000000000000000000..97093eba94f5347efcd3bb78658b367261b45839 GIT binary patch literal 164 zcmV;V09*fo00093A^;2W<>h7A<}+cJ zAa{yPo)|T4CfI`#6NmX>jSa2lsId3G^odE!MSR_FJ literal 0 HcmV?d00001 diff --git a/tests/files/tpm2/sign/sign/handle_pcr_5_6_7/public_key.pem b/tests/files/tpm2/sign/sign/handle_pcr_5_6_7/public_key.pem new file mode 100644 index 0000000..5427e6e --- /dev/null +++ b/tests/files/tpm2/sign/sign/handle_pcr_5_6_7/public_key.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEd4pMnhg1bSbYgxITh/lhjRHQRnig +/iOBggLPkpV6UfClAfKH7SNKS/76VqeGl8n/c6gDELebiicFUR5s7BiuzA== +-----END PUBLIC KEY----- diff --git a/tests/files/tpm2/sign/sign/handle_pcr_5_6_7/public_key.raw b/tests/files/tpm2/sign/sign/handle_pcr_5_6_7/public_key.raw new file mode 100644 index 0000000..f79db72 --- /dev/null +++ b/tests/files/tpm2/sign/sign/handle_pcr_5_6_7/public_key.raw @@ -0,0 +1 @@ +wL5m&؃aFx#ϒzQ#JKVs'Ql \ No newline at end of file diff --git a/tests/files/tpm2/sign/sign/pcr_4_7/ecc.priv b/tests/files/tpm2/sign/sign/pcr_4_7/ecc.priv new file mode 100644 index 0000000000000000000000000000000000000000..decea21e5dd567c8953de41630ed08edaf4e0812 GIT binary patch literal 128 zcmV-`0Du1gegGf^*_Mhf^ImAUiM;LRrEsJYJO2j|CkeN{%Iyr7!dwgG1i+4zl)59S3=VPgsnQ i#UGovQ(Y;T=RSM4Zp*nc<15h6o&Pa6#Yz literal 0 HcmV?d00001 diff --git a/tests/files/tpm2/sign/sign/pcr_4_7/ecc.pub b/tests/files/tpm2/sign/sign/pcr_4_7/ecc.pub new file mode 100644 index 0000000000000000000000000000000000000000..c0286031ad6cbb63583c26a6f0ef85e38fda3737 GIT binary patch literal 120 zcmV-;0Ehnob^s#)3jhQJG5{d-u$eredQV~FL#I~EIlDgPxDvEaD(kFDy~DokvC2UJ z5C9MW0{{>JAlVfITA5bYZIHm6KoOyWg{ZAmZliS&4FI#Fg2?RtRme=-07 literal 0 HcmV?d00001 diff --git a/tests/files/tpm2/sign/sign/pcr_4_7/pcrs b/tests/files/tpm2/sign/sign/pcr_4_7/pcrs new file mode 100644 index 0000000..f64ebbb --- /dev/null +++ b/tests/files/tpm2/sign/sign/pcr_4_7/pcrs @@ -0,0 +1 @@ +4,7 \ No newline at end of file diff --git a/tests/files/tpm2/sign/sign/pcr_4_7/public_key.der b/tests/files/tpm2/sign/sign/pcr_4_7/public_key.der new file mode 100644 index 0000000000000000000000000000000000000000..059b054d501cd61dd3c51a4a3a79b37ee4f09730 GIT binary patch literal 91 zcmXqrG!SNE*J|@PXUoLM#sOw9GqN)~F|gbeWsaH|c0G5(fjJI>3!7S3tPROqTq?lJ uws)$}w$cyGD~pUPjBZIyJe+=VjaTBIkDEQ%&un3d@eyWj=7GkFW5Wl7c*$+BN z_A2^Obr#-KHqtPSFaufVD=>9&_Un4$fYAkt>S_pbBq3$1SW3b9`YmS02H*J_kI|8W iPVJT-(*p$s69o%kv6yl>&Lo_=UpEj)m0IC=x^Z)<= literal 0 HcmV?d00001 diff --git a/tests/files/tpm2/sign/sign/plain/public_key.der b/tests/files/tpm2/sign/sign/plain/public_key.der new file mode 100644 index 0000000000000000000000000000000000000000..1b273fb7bc9be0f0a863583895f216ac8aae003e GIT binary patch literal 91 zcmXqrG!SNE*J|@PXUoLM#sOw9GqN)~F|cUytU75@aP9mAtK|t&Jt|Mz#T#Fr$z{D? vS{^hz{(#)X2EPdh`W81H{G||)e`VFT8)3`Woqn@*$+LqYnjMQzn^pk;z}Y9q literal 0 HcmV?d00001 diff --git a/tests/files/tpm2/sign/sign/plain/public_key.pem b/tests/files/tpm2/sign/sign/plain/public_key.pem new file mode 100644 index 0000000..dae72bc --- /dev/null +++ b/tests/files/tpm2/sign/sign/plain/public_key.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKgyqyTRw1s+QOqdgGowk5YcXgevM +bQXfdXdSm1/AHpGATpDAjqOBwfogWG/UqvbYVqeuy+y1pObBVCmIo8s1eg== +-----END PUBLIC KEY----- diff --git a/tests/files/tpm2/sign/sign/plain/public_key.raw b/tests/files/tpm2/sign/sign/plain/public_key.raw new file mode 100644 index 0000000..4063f71 --- /dev/null +++ b/tests/files/tpm2/sign/sign/plain/public_key.raw @@ -0,0 +1 @@ +* 4pϐ:`$muwR_N XoԪV쵤T)5z \ No newline at end of file diff --git a/tests/run_test.sh b/tests/run_test.sh new file mode 100755 index 0000000..16e1231 --- /dev/null +++ b/tests/run_test.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +bash tests/run_with_test_tpm2.sh cargo test --workspace --verbose "${@}" +# cargo test --workspace --verbose --locked diff --git a/tests/run_with_test_tpm2.sh b/tests/run_with_test_tpm2.sh new file mode 100755 index 0000000..894d00e --- /dev/null +++ b/tests/run_with_test_tpm2.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -e -x +export TCTI="tabrmd:bus_type=session" +export TPM2TOOLS_TCTI="${TCTI}" +DIR="$(pwd)/tests/swtpm" +TMPDIR="$(mktemp -d)" +KILL_PIDS=() +cleanup_pids() { + for pid in "${KILL_PIDS[@]}"; do + if kill -0 "${pid}" &> /dev/null; then + kill "${pid}" || true + fi + done +} +cleanup() { + local pid + swtpm_ioctl --tcp 127.0.0.1:2322 -s + cleanup_pids + cleanup_pids -9 + rm -rf "${TMPDIR}" +} +trap cleanup EXIT +cp -r "${DIR}" "${TMPDIR}" +# To create a new persistent state: +# swtpm_setup --tpm2 --tpmstate "${DIR}" --pcr-banks sha1,sha256 --display +swtpm socket \ + --tpm2 \ + --tpmstate dir="${TMPDIR}/swtpm" \ + --flags startup-clear \ + --ctrl type=tcp,port=2322 \ + --server type=tcp,port=2321 & +KILL_PIDS+=("$!") +sleep 0.1 # Wait until started +# in gitlab container, tests are running as root +/usr/sbin/tpm2-abrmd --session --allow-root --tcti=swtpm & +KILL_PIDS+=("$!") +sha256="0000000000000000000000000000000000000000000000000000000000000000" +tpm2_pcrextend \ + "5:sha256=0x${sha256}" \ + "7:sha256=0x${sha256}" +"${@}" diff --git a/tests/swtpm/tpm2-00.permall b/tests/swtpm/tpm2-00.permall new file mode 100644 index 0000000000000000000000000000000000000000..140acf2e2d203f1258a731eb2542916370746bf1 GIT binary patch literal 2731 zcmb_e2~^Bm7{CAjj7n2!8BL4$*ASkQN{f?q2#uPA$s{i+Y4KiZ6P~syEvKTP*PfB_EbYKSr>PHLcDwfgue}kU_3S9m^6~KjKgs-f}!=MjY0u9(B zs6ZeY13`jdNP09zkcE)Mm52LB%jF;>Veq1Ii9#9n3<+dlj}k=dkTVy80RWCP(m6sk zibFt%*P;osBa!Z<$M3E0TcrON()#~BM#0Fcx~7&WFJGPxRE`2^DfB5M}|Dd<_E(KKw0Hb%PMu&5~p+n0CXB$|SCg0XE2{U=R z$G}tZQqx-Ehj(*4RomwUJJFcyBZ|~+zYN|iU&Cv4W*vRqkVu`!(Jlmxi)K7cH=mM$ zrcJ?D>~ADT`LNwx98YR%Rg@Ry7TVh0d&m!|vn)Pro$SVr3=64K&j{MjbnHCc78)mN zoJMI(0E~#vosq>`c3nAAtXx&OIj*_Sx_O(ab0}YTa&^1P!StRqry)TFUC{TyzhXkG z)!eMrB2!W}>(>qsmCmi*Q`7t;$AT(`jKLztX+J;GhsA)wLzC1F8IVaMBO`z!ZWT~5 z14VJG1PCb|L$8=CmMCM;@R8P9@N+GPrD?t3nJmPe0UR&&i*cy3B>E(wFx2}7c&?-_+jeU{N#fEY2x)Esmx zTWrS91mhhoH5TdK33L9t<5UXBzrSR4kLw4tF09_Py>$1b%UOaa zPB*mtA}Cq*3_v6$QA0<{UeN^s+7APhQ={W3>>7br0QZRBfRI|9bR98{^7@JpJ!bQQ zE)82X+2*wSD;;fC7rO8~lB+|A23}q1;h160_hJ0jROi;M8)l@$I!z-d#Ht%lkAJY| zP|h3545rCdxy3uxA>$Z28O2ne^Ml;3$q&{kHigXYEsblLUUu&41=%^jD?F^P^$P!l zar_I6e^kS3z1uv8uH41DN-kEh*1YU)F1P5wtgi|)g)iJsG8ggy;XvOYFuR`@Rlz+g z>r>*nU~ytfvgRF){-mQZ{Cxsy^k{4NAV)Ud);`AQsp)P9Cb2hdA;Y(5cfgx2fv?^> zlPk8m--pqAfm;gH%*>Cs-D-{1kN565Gs#@%T>mHT5 zWjnG&^oU5jHYsK2s#k!>#d>19=+-u(K~7#wTGdEkK1um3KYr?%RTI6RYaF{Z+8S1g z;oIX&x= PX8gnL#Ki9i`cwK3L@=|0 literal 0 HcmV?d00001 diff --git a/usr/bin/cryptographic_id_add_initramfs b/usr/bin/cryptographic_id_add_initramfs index 4b490f2..7834215 100755 --- a/usr/bin/cryptographic_id_add_initramfs +++ b/usr/bin/cryptographic_id_add_initramfs @@ -149,7 +149,8 @@ tpm2) -pubout -out "${key}/public_key.der" > /dev/null dd if="${key}/public_key.der" of="${key}/public_key.raw" \ bs=1 skip=26 count=65 status=none - hash="$(openssl dgst -sha256 -c --hex < "${key}/public_key.raw" | \ + hash="$(dd if="${key}/public_key.raw" bs=1 skip=1 | \ + openssl dgst -sha256 -c --hex | \ tr '[:lower:]' '[:upper:]')" printf "%s\\n\\n" "${hash}" printf "Fingerprint:\\n" diff --git a/usr/lib/cryptographic_id/initramfs_helper b/usr/lib/cryptographic_id/initramfs_helper index d6f5eaf..33344f7 100755 --- a/usr/lib/cryptographic_id/initramfs_helper +++ b/usr/lib/cryptographic_id/initramfs_helper @@ -1,20 +1,8 @@ #!/usr/bin/sh TMPDIR="/tmp/cryptographic_id" -KEY="${TMPDIR}/key" -MSG="${TMPDIR}/message" -SIG="${TMPDIR}/signature" QRCODE="${TMPDIR}/qrcode" -run_cmd() { +KEY="$(systemd-escape --unescape "${1}")" +run_binary() { /usr/lib/cryptographic_id/cryptographic-id-rs "${@}" } -case "${1}" in -sign) - run_cmd "${1}" "${KEY}" "$(cat "${MSG}")" > "${QRCODE}" -;; -tpm2_build) - run_cmd "${1}" "${KEY}" "${MSG}" "${TMPDIR}" -;; -tpm2_show) - run_cmd "${1}" "${SIG}" "${TMPDIR}" > "${QRCODE}" -;; -esac +run_binary sign "${KEY}" "$(cat "${MSG}")" > "${QRCODE}" diff --git a/usr/lib/cryptographic_id/show_identities b/usr/lib/cryptographic_id/show_identities index c40cf87..1d77f6c 100755 --- a/usr/lib/cryptographic_id/show_identities +++ b/usr/lib/cryptographic_id/show_identities @@ -36,8 +36,13 @@ cryptsetup_identity() { /usr/lib/systemd/systemd-cryptsetup detach cryptographic_id_tmp } -sign_ed25519() { - systemctl start cryptographic_id_helper@sign.service +sign_helper_tmpkey() { + sign_helper "${TMPKEY}" +} + +sign_helper() { + param="$(systemd-escape "${1}")" + systemctl start "cryptographic_id_helper@${param}.service" } tpm2_identity() { @@ -45,64 +50,6 @@ tpm2_identity() { cp "${1}/public_key.raw" "${TMPKEY}" } -ATTR="fixedtpm|fixedparent|sensitivedataorigin|userwithauth|restricted|decrypt" -sign_tpm2() { - dir="${1}" - auth="str:x" - policy="" - if [ -f "${dir}/pcrs" ]; then - pcrs="$(cat "${dir}/pcrs")" - tpm2_startauthsession \ - -S "${TMPDIR}/session.ctx" \ - --policy-session - tpm2_policypcr \ - --quiet \ - --policy "${TMPDIR}/policy.pcr" \ - --session "${TMPDIR}/session.ctx" \ - --pcr-list "sha256:${pcrs}" - policy="${TMPDIR}/policy.pcr" - auth="session:${TMPDIR}/session.ctx" - fi - handle="${TMPDIR}/handle" - if [ -f "${dir}/handle" ]; then - handle="${dir}/handle" - else - tpm2_createprimary \ - --quiet \ - --hierarchy e \ - --hash-algorithm sha256 \ - --key-algorithm ecc256 \ - --key-context "${TMPDIR}/primary.ctx" \ - ${policy:+"--policy"} ${policy:+"${policy}"} \ - --attributes "${ATTR}" - tpm2_load \ - --quiet \ - --parent-context "${TMPDIR}/primary.ctx" \ - --public "${dir}/ecc.pub" \ - --private "${dir}/ecc.priv" \ - --key-context "${handle}" - fi - systemctl start cryptographic_id_helper@tpm2_build.service - tpm2_sign \ - --key-context "${handle}" \ - --hash-algorithm sha256 \ - --scheme ecdsa \ - --format plain \ - --auth "${auth}" \ - --signature "${TMPDIR}/signature" \ - "${TMPDIR}/to_sign.bin" - if [ -f "${TMPDIR}/session.ctx" ]; then - tpm2_flushcontext "${TMPDIR}/session.ctx" - fi - rm -f "${TMPDIR}/ecc.ctx" "${TMPDIR}/primary.ctx" - if [ -f "${dir}/pcrs" ]; then - rm -f "${TMPDIR}/policy.pcr" "${TMPDIR}/session.ctx" - fi - systemctl start cryptographic_id_helper@tpm2_show.service - rm -f "${TMPDIR}/signature" "${TMPDIR}/message" - rm -f "${TMPDIR}/to_sign.bin" "${TMPDIR}/id.bin" -} - show_identities() { res="" get_key="${2}" @@ -146,10 +93,10 @@ cleanup() { main() { trap cleanup EXIT - show_identities "insecure" cat_identity sign_ed25519 - show_identities "tpm2" tpm2_identity sign_tpm2 - show_identities "cryptsetup" cryptsetup_identity sign_ed25519 - show_identities "age" age_decrypt_identity sign_ed25519 + show_identities "insecure" cat_identity sign_helper + show_identities "tpm2" tpm2_identity sign_helper + show_identities "cryptsetup" cryptsetup_identity sign_helper_tmpkey + show_identities "age" age_decrypt_identity sign_helper_tmpkey } main diff --git a/usr/lib/dracut/modules.d/90cryptographic-id/module-setup.sh b/usr/lib/dracut/modules.d/90cryptographic-id/module-setup.sh index 97c9f55..4956aa2 100644 --- a/usr/lib/dracut/modules.d/90cryptographic-id/module-setup.sh +++ b/usr/lib/dracut/modules.d/90cryptographic-id/module-setup.sh @@ -24,6 +24,7 @@ install() { inst_binary mkdir inst_binary mount inst_binary rm + inst_binary systemd-escape inst_binary umount inst_binary /usr/lib/cryptographic_id/cryptographic-id-rs inst /usr/lib/cryptographic_id/initramfs_helper diff --git a/usr/lib/initcpio/install/cryptographic-id b/usr/lib/initcpio/install/cryptographic-id index 4b9fbf8..1a2d7a0 100644 --- a/usr/lib/initcpio/install/cryptographic-id +++ b/usr/lib/initcpio/install/cryptographic-id @@ -7,6 +7,7 @@ build() { add_binary mkdir add_binary mount add_binary rm + add_binary systemd-escape add_binary umount add_binary "/usr/lib/cryptographic_id/cryptographic-id-rs" add_file "/usr/lib/cryptographic_id/initramfs_helper" diff --git a/usr/lib/systemd/system/cryptographic_id.service b/usr/lib/systemd/system/cryptographic_id.service index 56d5a49..df19695 100644 --- a/usr/lib/systemd/system/cryptographic_id.service +++ b/usr/lib/systemd/system/cryptographic_id.service @@ -1,10 +1,11 @@ [Unit] Description=Show cryptographic identities on boot DefaultDependencies=no +After=dev-tpmrm0.device After=systemd-vconsole-setup.service After=tmp.mount -Wants=tmp.mount Wants=systemd-vconsole-setup.service +Wants=tmp.mount # Dracut After=dracut-initqueue.service Before=dracut-pre-mount.service diff --git a/usr/lib/systemd/system/cryptographic_id_helper@.service b/usr/lib/systemd/system/cryptographic_id_helper@.service index 666fe0b..259eb3d 100644 --- a/usr/lib/systemd/system/cryptographic_id_helper@.service +++ b/usr/lib/systemd/system/cryptographic_id_helper@.service @@ -1,16 +1,21 @@ [Unit] Description=Sandbox helper for cryptographic id DefaultDependencies=no +After=dev-tpmrm0.device [Service] Type=oneshot ExecStart=-/usr/bin/sh /usr/lib/cryptographic_id/initramfs_helper %I TemporaryFileSystem=/:rw +BindReadOnlyPaths=/etc/cryptographic_id BindReadOnlyPaths=/usr BindReadOnlyPaths=/lib BindReadOnlyPaths=/lib64 BindPaths=/tmp/cryptographic_id +BindPaths=-/dev/tpmrm0 +DeviceAllow=/dev/tpmrm0 +Environment=TCTI=device:/dev/tpmrm0 AmbientCapabilities= CapabilityBoundingSet=