From a6d6602a464a5efe4053cb3c58f132f6781ed2d3 Mon Sep 17 00:00:00 2001 From: Dmitry Zolotukhin Date: Fri, 12 Jul 2024 17:39:59 +0200 Subject: [PATCH] Switched from RustCrypto to OpenSSL. * OpenSSL supports a lot more ciphers and algorithms. It also has a lot more hardware-level optimizations like AVX and Neon. * OpenSSL can be updated (patched) without rebuilding the app. * OpenSSL has a more active maintenance cycle and is likely the first one to receive security patches. * OpenSSL (or its forks) are used by most Rust TLS implementations. * RustCrypto's TLS client is in its infancy, with certificate validation being especially unpolished. --- .github/workflows/cargo-build.yml | 29 +- Cargo.lock | 611 +++-------------------------- Cargo.toml | 14 +- src/ikev2/crypto.rs | 615 ++++++++++++++++-------------- src/ikev2/mod.rs | 51 ++- src/ikev2/pki.rs | 236 +++++------- 6 files changed, 523 insertions(+), 1033 deletions(-) diff --git a/.github/workflows/cargo-build.yml b/.github/workflows/cargo-build.yml index aa171d3..772a4b5 100644 --- a/.github/workflows/cargo-build.yml +++ b/.github/workflows/cargo-build.yml @@ -26,6 +26,7 @@ jobs: ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git/db + ~/.cargo/bin ${{ github.workspace }}/target key: lint-${{ runner.os }}-${{ steps.get-rust-version.outputs.VERSION }}-${{ hashFiles('Cargo.*') }} @@ -33,14 +34,14 @@ jobs: run: cargo clippy - name: Install cargo-edit + if: ${{ steps.cache-rust.outputs.cache-hit != 'true' }} run: cargo install cargo-edit - name: Check for new dependency versions run: cargo upgrade --dry-run build-linux: - # Older Ubuntu versions will link with an older GLIBC and provide better compatibility - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest needs: validate strategy: fail-fast: true @@ -72,15 +73,33 @@ jobs: - name: Install compiler and prepare for cross-compilation if: matrix.arch == 'aarch64' run: | - rustup target add ${{ matrix.arch }}-unknown-linux-gnu - sudo apt-get update sudo apt-get install -y crossbuild-essential-arm64 qemu-user-static - echo "LD_LIBRARY_PATH=/usr/aarch64-linux-gnu/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV + sudo dpkg --add-architecture arm64 + sudo sed 's/deb http/deb \[arch=amd64,i386\] http/' -i /etc/apt/sources.list + echo | sudo tee -a /etc/apt/sources.list <> $GITHUB_ENV + echo "OPENSSL_LIB_DIR=/usr/lib/aarch64-linux-gnu" >> $GITHUB_ENV + echo "OPENSSL_INCLUDE_DIR=/usr/include" >> $GITHUB_ENV echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/usr/bin/aarch64-linux-gnu-gcc" >> $GITHUB_ENV echo "CC=/usr/bin/aarch64-linux-gnu-gcc" >> $GITHUB_ENV echo "CXX=/usr/bin/aarch64-linux-gnu-g++" >> $GITHUB_ENV echo "CFLAGS=-march=armv8.2-a" >> $GITHUB_ENV echo "CXXFLAGS=-march=armv8.2-a" >> $GITHUB_ENV + rustup target add ${{ matrix.arch }}-unknown-linux-gnu + + - name: Install OpenSSL development packages + run: | + ARCH=amd64 + if [ "${{matrix.arch}}" == "aarch64" ]; then + ARCH=arm64 + fi + sudo apt-get install libssl-dev:${ARCH} - name: Build run: cargo build --target=${{ matrix.arch }}-unknown-linux-gnu --release diff --git a/Cargo.lock b/Cargo.lock index ddf5265..c1677d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,47 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - [[package]] name = "backtrace" version = "0.3.73" @@ -74,40 +33,10 @@ dependencies = [ ] [[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - -[[package]] -name = "byteorder" -version = "1.5.0" +name = "bitflags" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bytes" @@ -115,15 +44,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - [[package]] name = "cc" version = "1.0.104" @@ -137,154 +57,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "cpufeatures" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "der" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" -dependencies = [ - "const-oid", - "der_derive", - "flagset", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "der_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "ecdsa" -version = "0.16.9" +name = "foreign-types" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", + "foreign-types-shared", ] [[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "rand_core", - "sec1", - "subtle", - "zeroize", -] - -[[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 = "flagset" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb3aa5e95cf9aabc17f060cfa0ced7b83f042390760ca53bf09df9968acaa1" - -[[package]] -name = "generic-array" -version = "0.14.7" +name = "foreign-types-shared" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "getrandom" @@ -297,73 +82,18 @@ dependencies = [ "wasi", ] -[[package]] -name = "ghash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" -dependencies = [ - "opaque-debug", - "polyval", -] - [[package]] name = "gimli" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" -[[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 = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - [[package]] name = "log" version = "0.4.22" @@ -396,53 +126,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - [[package]] name = "object" version = "0.36.1" @@ -453,70 +136,60 @@ dependencies = [ ] [[package]] -name = "opaque-debug" -version = "0.3.1" +name = "once_cell" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] -name = "p256" -version = "0.13.2" +name = "openssl" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", ] [[package]] -name = "pem-rfc7468" -version = "0.7.0" +name = "openssl-macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "base64ct", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "pkcs1" -version = "0.7.5" +name = "openssl-sys" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ - "der", - "pkcs8", - "spki", + "cc", + "libc", + "pkg-config", + "vcpkg", ] [[package]] -name = "pkcs8" -version = "0.10.2" +name = "pin-project-lite" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] -name = "polyval" -version = "0.6.2" +name = "pkg-config" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "ppv-lite86" @@ -524,15 +197,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - [[package]] name = "proc-macro2" version = "1.0.86" @@ -546,21 +210,10 @@ dependencies = [ name = "pterodapter" version = "0.0.1" dependencies = [ - "aes", - "aes-gcm", - "cbc", - "cipher", - "crypto-bigint", - "der", - "hmac", "log", - "p256", + "openssl", "rand", - "rsa", - "sha1", - "sha2", "tokio", - "x509-cert", ] [[package]] @@ -602,77 +255,12 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "rsa" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core", - "signature", - "spki", - "subtle", - "zeroize", -] - [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "subtle", - "zeroize", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -682,22 +270,6 @@ dependencies = [ "libc", ] -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - [[package]] name = "socket2" version = "0.5.7" @@ -708,28 +280,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - [[package]] name = "syn" version = "2.0.70" @@ -741,27 +291,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tls_codec" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e78c9c330f8c85b2bae7c8368f2739157db9991235123aa1b15ef9502bfb6a" -dependencies = [ - "tls_codec_derive", - "zeroize", -] - -[[package]] -name = "tls_codec_derive" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tokio" version = "1.38.0" @@ -778,12 +307,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -791,20 +314,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "version_check" -version = "0.9.4" +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "wasi" @@ -950,35 +463,3 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "x509-cert" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" -dependencies = [ - "const-oid", - "der", - "spki", - "tls_codec", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/Cargo.toml b/Cargo.toml index 2e1e196..405e97a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,19 +11,7 @@ rust-version = "1.77" log = { version = "0.4", default-features = false } tokio = { version = "1.38", default-features = false, features = ["rt", "io-util", "signal", "net", "time", "sync"] } rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] } - -sha1 = { version = "0.10", default-features = false, features = ["oid"] } -p256 = { version = "0.13", default-features = false, features = ["arithmetic", "ecdsa"] } -hmac = { version = "0.12", default-features = false, features = ["reset"] } -sha2 = { version = "0.10", default-features = false } -aes = { version = "0.8", default-features = false } -aes-gcm = { version = "0.10", default-features = false, features = ["aes"] } -crypto-bigint = { version = "*", default-features = false, features = ["rand"] } -der = { version = "*", default-features = false } -x509-cert = { version = "*", default-features = false, features = ["pem", "std"] } -rsa = { version = "*", default-features = false, features = ["pem", "std"] } -cipher = { version = "*", default-features = false, features = ["block-padding"] } -cbc = { version = "*", default-features = false } +openssl = { version = "0.10", default-features = false } [profile.release] strip = true diff --git a/src/ikev2/crypto.rs b/src/ikev2/crypto.rs index a8cf8b4..4fe8e3a 100644 --- a/src/ikev2/crypto.rs +++ b/src/ikev2/crypto.rs @@ -1,30 +1,10 @@ -use aes::{ - cipher::{BlockDecryptMut, BlockEncryptMut}, - Aes256, -}; -use aes_gcm::{ - aead::{AeadMutInPlace, Buffer}, - Aes256Gcm, Nonce, -}; -use cipher::{block_padding, BlockSizeUser, InnerIvInit, Iv, IvSizeUser}; -use crypto_bigint::{ - modular::constant_mod::{self, ResidueParams}, - Encoding, -}; -use hmac::{Hmac, Mac}; use log::debug; -use p256::{ - elliptic_curve::sec1::Tag as P256Tag, EncodedPoint, NonZeroScalar, ProjectivePoint, PublicKey, -}; +use openssl::{bn, cipher, cipher_ctx, derive, dh, ec, hash, nid, pkey, sign}; use rand::Rng; -use sha2::Sha256; use std::{error, fmt, ops::Range}; -use crypto_bigint::{const_residue, impl_modulus, rand_core::OsRng, Random, U1024}; - use super::message; -const MAX_DH_KEY_LENGTH: usize = 1024 / 8; const MAX_PRF_KEY_LENGTH: usize = 256 / 8; const MAX_AUTH_KEY_LENGTH: usize = 256 / 8; const MAX_ENCRYPTION_KEY_LENGTH: usize = 256 / 8; @@ -63,7 +43,7 @@ impl TransformParameters { DHTransformType::init( self.dh .as_ref() - .ok_or_else(|| InitError::new("DH not configured"))? + .ok_or_else(|| "DH not configured")? .transform_type, ) } @@ -72,7 +52,7 @@ impl TransformParameters { PseudorandomTransform::init( self.prf .as_ref() - .ok_or_else(|| InitError::new("PRF not configured"))? + .ok_or_else(|| "PRF not configured")? .transform_type, key, ) @@ -275,35 +255,13 @@ pub fn choose_sa_parameters<'a>( .next() } -pub struct Array { - data: [u8; M], - len: usize, -} - -impl Array { - pub fn new(len: usize) -> Array { - Array { - data: [0u8; M], - len, - } - } - - pub fn as_slice(&self) -> &[u8] { - &self.data[..self.len] - } - - fn as_mut_slice(&mut self) -> &mut [u8] { - &mut self.data[..self.len] - } -} - pub enum DHTransformType { MODP1024(DHTransformMODP1024), ECP256(DHTransformECP256), } pub trait DHTransform { - fn read_public_key(&self) -> Array; + fn read_public_key(&self) -> &[u8]; fn key_length_bytes(&self) -> usize; @@ -311,28 +269,58 @@ pub trait DHTransform { fn group_number(&self) -> u16; - fn compute_shared_secret( - &self, - other_public_key: &[u8], - ) -> Result, InitError>; + fn compute_shared_secret(&self, other_public_key: &[u8]) -> Result, InitError>; } impl DHTransformType { fn init(transform_type: message::TransformType) -> Result { match transform_type { message::TransformType::DH_1024_MODP => { - let private_key = U1024::random(&mut OsRng); - // This calculates DH_MODP_GENERATOR_1024^private_key mod DHModulus1024. - let public_key = DH_MODP_RESIDUE_1024.pow(&private_key).retrieve(); + let dh_instance = + dh::Dh::params_from_pem(DH_PARAMS_MODP_1024.as_bytes()).map_err(|err| { + debug!("Failed to init DH MODP 1024 instance: {}", err); + "Failed to init DH MODP 1024 instance" + })?; + let private_key = dh_instance.generate_key().map_err(|err| { + debug!("Failed to generate DH MODP 1024 key: {}", err); + "Failed to generate DH MODP 1024 key" + })?; + let public_key = private_key.public_key().to_vec(); Ok(DHTransformType::MODP1024(DHTransformMODP1024 { private_key, public_key, })) } message::TransformType::DH_256_ECP => { - let private_key = NonZeroScalar::random(&mut OsRng); - let public_key = PublicKey::from_secret_scalar(&private_key); + let group = + ec::EcGroup::from_curve_name(nid::Nid::X9_62_PRIME256V1).map_err(|err| { + debug!("Failed to init DH ECP 256 instance: {}", err); + "Failed to init DH ECP 256 instance" + })?; + let private_key = ec::EcKey::generate(&group).map_err(|err| { + debug!("Failed to generate DH ECP 256 key: {}", err); + "Failed to generate DH ECP 256 key" + })?; + let mut ctx = bn::BigNumContext::new().map_err(|err| { + debug!( + "Failed to init math context for generating DH ECP 256 public key: {}", + err + ); + "Failed to init math context for generating DH ECP 256 public key" + })?; + let public_key = private_key + .public_key() + .to_bytes(&group, ec::PointConversionForm::UNCOMPRESSED, &mut ctx) + .map_err(|err| { + debug!("Failed to generate DH ECP 256 public key: {}", err); + "Failed to generate DH ECP 256 public key" + })?; + let private_key = pkey::PKey::from_ec_key(private_key).map_err(|err| { + debug!("Failed to convert DH ECP 256 private key: {}", err); + "Failed to convert DH ECP 256 private key" + })?; Ok(DHTransformType::ECP256(DHTransformECP256 { + group, private_key, public_key, })) @@ -343,7 +331,7 @@ impl DHTransformType { } impl DHTransform for DHTransformType { - fn read_public_key(&self) -> Array { + fn read_public_key(&self) -> &[u8] { match self { Self::MODP1024(ref dh) => dh.read_public_key(), Self::ECP256(ref dh) => dh.read_public_key(), @@ -371,10 +359,7 @@ impl DHTransform for DHTransformType { } } - fn compute_shared_secret( - &self, - other_public_key: &[u8], - ) -> Result, InitError> { + fn compute_shared_secret(&self, other_public_key: &[u8]) -> Result, InitError> { match self { Self::MODP1024(ref dh) => dh.compute_shared_secret(other_public_key), Self::ECP256(ref dh) => dh.compute_shared_secret(other_public_key), @@ -383,16 +368,13 @@ impl DHTransform for DHTransformType { } pub struct DHTransformMODP1024 { - public_key: U1024, - private_key: U1024, + private_key: dh::Dh, + public_key: Vec, } impl DHTransform for DHTransformMODP1024 { - fn read_public_key(&self) -> Array { - let mut res = Array::new(self.key_length_bytes()); - res.as_mut_slice() - .copy_from_slice(&self.public_key.to_be_bytes()); - res + fn read_public_key(&self) -> &[u8] { + &self.public_key } fn key_length_bytes(&self) -> usize { @@ -407,34 +389,36 @@ impl DHTransform for DHTransformMODP1024 { message::TransformType::DH_1024_MODP.type_id().1 } - fn compute_shared_secret( - &self, - other_public_key: &[u8], - ) -> Result, InitError> { - let mut res = Array::new(self.shared_key_length_bytes()); + fn compute_shared_secret(&self, other_public_key: &[u8]) -> Result, InitError> { if other_public_key.len() != self.key_length_bytes() { return Err("MODP 1024 key length is not valid".into()); } - let other_public_key = U1024::from_be_slice(other_public_key); - let other_key_residue = const_residue!(other_public_key, DHModulus1024); - let shared_key = other_key_residue.pow(&self.private_key).retrieve(); - res.as_mut_slice() - .copy_from_slice(&shared_key.to_be_bytes()); - Ok(res) + let other_public_key = bn::BigNum::from_slice(other_public_key).map_err(|err| { + debug!( + "Failed to init math context to derive DH MODP 1024 shared key: {}", + err + ); + "Failed to init math context to derive DH MODP 1024 shared key" + })?; + Ok(self + .private_key + .compute_key(&other_public_key) + .map_err(|err| { + debug!("Failed to compute DH MODP 1024 shared key: {}", err); + "Failed to compute DH MODP 1024 shared key" + })?) } } pub struct DHTransformECP256 { - private_key: NonZeroScalar, - public_key: PublicKey, + group: ec::EcGroup, + private_key: pkey::PKey, + public_key: Vec, } impl DHTransform for DHTransformECP256 { - fn read_public_key(&self) -> Array { - let mut res = Array::new(self.key_length_bytes()); - res.as_mut_slice() - .copy_from_slice(&EncodedPoint::from(&self.public_key).as_bytes()[1..]); - res + fn read_public_key(&self) -> &[u8] { + &self.public_key[1..] } fn key_length_bytes(&self) -> usize { @@ -449,33 +433,55 @@ impl DHTransform for DHTransformECP256 { message::TransformType::DH_256_ECP.type_id().1 } - fn compute_shared_secret( - &self, - other_public_key: &[u8], - ) -> Result, InitError> { - let mut res = Array::new(self.shared_key_length_bytes()); + fn compute_shared_secret(&self, other_public_key: &[u8]) -> Result, InitError> { let mut other_public_key_sec1 = [0u8; 1 + 64]; - other_public_key_sec1[0] = P256Tag::Uncompressed.into(); + // Uncompressed form. + other_public_key_sec1[0] = 0x04; other_public_key_sec1[1..].copy_from_slice(other_public_key); - let other_public_key = match PublicKey::from_sec1_bytes(&other_public_key_sec1) { - Ok(key) => key, - Err(err) => { - debug!("Failed to decode other public key {}", err); - return Err("Failed to decode other public key".into()); - } - }; - let public_point = ProjectivePoint::from(other_public_key.as_affine()); - let secret_point = (&public_point * &self.private_key).to_affine(); - res.as_mut_slice() - .copy_from_slice(&EncodedPoint::from(secret_point).compress().as_bytes()[1..]); - Ok(res) + let mut ctx = bn::BigNumContext::new().map_err(|err| { + debug!( + "Failed to init math context to derive DH ECP 256 shared key: {}", + err + ); + "Failed to init math context to derive DH ECP 256 shared key" + })?; + let other_public_key = + ec::EcPoint::from_bytes(&self.group, &other_public_key_sec1, &mut ctx).map_err( + |err| { + debug!( + "Failed to convert DH ECP 256 public key into point: {}", + err + ); + "Failed to convert DH ECP 256 public key into point" + }, + )?; + let other_key: pkey::PKey<_> = ec::EcKey::from_public_key(&self.group, &other_public_key) + .map_err(|err| { + debug!("Failed to convert DH ECP 256 public key: {}", err); + "Failed to convert DH ECP 256 public key" + })? + .try_into() + .map_err(|err| { + debug!("Failed to convert DH ECP 256 public key: {}", err); + "Failed to convert DH ECP 256 public key" + })?; + let mut deriver = derive::Deriver::new(&self.private_key).map_err(|err| { + debug!("Failed to create DH ECP 256 deriver: {}", err); + "Failed to create DH ECP 256 deriver" + })?; + deriver.set_peer(&other_key).map_err(|err| { + debug!("Failed to set DH ECP 256 peer: {}", err); + "Failed to set DH ECP 256 peer" + })?; + Ok(deriver.derive_to_vec().map_err(|err| { + debug!("Failed to compute DH ECP 256 shared key: {}", err); + "Failed to compute DH ECP 256 shared key" + })?) } } -type HmacSha256 = Hmac; - pub enum PseudorandomTransform { - HmacSha256(HmacSha256), + HmacSha256(pkey::PKey), } impl PseudorandomTransform { @@ -485,25 +491,27 @@ impl PseudorandomTransform { ) -> Result { match transform_type { message::TransformType::PRF_HMAC_SHA2_256 => { - let core_wrapper = HmacSha256::new_from_slice(key) - .map_err(|_| InitError::new("Failed to init HMAC SHA256 PRF"))?; - let hmac = core_wrapper; - Ok(Self::HmacSha256(hmac)) + let key = pkey::PKey::hmac(key).map_err(|err| { + debug!("Failed to init SHA256 HMAC key: {}", err); + "Failed to init SHA256 HMAC key" + })?; + Ok(Self::HmacSha256(key)) } _ => Err("Unsupported PRF".into()), } } - pub fn prf(&self, data: &[u8]) -> Array { + pub fn prf(&self, data: &[u8]) -> Result, CryptoError> { match self { - Self::HmacSha256(ref hmac) => { - const BLOCK_LENGTH: usize = 256 / 8; - let mut hmac = hmac.clone(); - hmac.update(data); - let hash = hmac.finalize().into_bytes(); - let mut result = Array::new(BLOCK_LENGTH); - result.as_mut_slice().copy_from_slice(&hash); - result + Self::HmacSha256(ref key) => { + let mut signer = new_hmac_sha256(key).map_err(|err| { + debug!("Failed to init SHA256 HMAC signer: {}", err); + "Failed to init SHA256 HMAC signer" + })?; + Ok(signer.sign_oneshot_to_vec(data).map_err(|err| { + debug!("Failed to sign data with SHA256 HMAC: {}", err); + "Failed to sign data with SHA256 HMAC" + })?) } } } @@ -515,16 +523,24 @@ impl PseudorandomTransform { ) -> Result { let mut keys = DerivedKeys::new(params); match self { - Self::HmacSha256(ref hmac) => { - let mut hmac = hmac.clone(); + Self::HmacSha256(ref key) => { + // Can OpenSSL's HKDF be used here? + let mut signer = new_hmac_sha256(key).map_err(|err| { + debug!("Failed to init SHA256 HMAC signer: {}", err); + "Failed to init SHA256 HMAC signer" + })?; let mut next_data = vec![0u8; data.len() + params.prf_key_length() / 8 + 1]; let mut cursor = 0; // First T1 chunk. next_data[0..data.len()].copy_from_slice(data); next_data[data.len()] = 1; - hmac.update(&next_data[0..data.len() + 1]); + let mut hash = signer + .sign_oneshot_to_vec(&next_data[0..data.len() + 1]) + .map_err(|err| { + debug!("Failed to sign PRF+ chunk: {}", err); + "Failed to sign PRF+ chunk" + })?; for t in 1..255 { - let hash = hmac.finalize_reset().into_bytes(); let dest_range = cursor..(cursor + hash.len()).min(keys.full_length()); let src_range = ..dest_range.len(); cursor = dest_range.end; @@ -536,7 +552,14 @@ impl PseudorandomTransform { next_data[0..hash.len()].copy_from_slice(&hash); next_data[hash.len()..hash.len() + data.len()].copy_from_slice(&data); next_data[hash.len() + data.len()] = t + 1; - hmac.update(&next_data); + let mut signer = new_hmac_sha256(key).map_err(|err| { + debug!("Failed to init SHA256 HMAC signer: {}", err); + "Failed to init SHA256 HMAC signer" + })?; + hash = signer.sign_oneshot_to_vec(&next_data).map_err(|err| { + debug!("Failed to sign PRF+ chunk: {}", err); + "Failed to sign PRF+ chunk" + })?; } CryptoStack::new(params, &keys) } @@ -587,17 +610,18 @@ impl DerivedKeys { enum Auth { None, - HmacSha256tr128(HmacSha256), + HmacSha256tr128(pkey::PKey), } impl Auth { fn init(transform_type: Option, key: &[u8]) -> Result { match transform_type { Some(message::TransformType::AUTH_HMAC_SHA2_256_128) => { - let core_wrapper = HmacSha256::new_from_slice(key) - .map_err(|_| InitError::new("Failed to init HMAC SHA256-128 AUTH"))?; - let hmac = core_wrapper; - Ok(Self::HmacSha256tr128(hmac)) + let key = pkey::PKey::hmac(key).map_err(|err| { + debug!("Failed to init SHA256-128 HMAC key: {}", err); + "Failed to init SHA256-128 HMAC key" + })?; + Ok(Self::HmacSha256tr128(key)) } None => Ok(Self::None), _ => Err("Unsupported PRF".into()), @@ -606,7 +630,7 @@ impl Auth { pub fn sign(&self, data: &mut [u8]) -> Result<(), CryptoError> { match self { - Self::HmacSha256tr128(ref hmac) => { + Self::HmacSha256tr128(ref key) => { let signature_length = self.signature_length(); if data.len() < signature_length { return Err("Not enough space to add signature".into()); @@ -614,9 +638,14 @@ impl Auth { let data_length = data.len() - signature_length; let hash = { let sign_data = &data[..data_length]; - let mut hmac = hmac.clone(); - hmac.update(sign_data); - hmac.finalize().into_bytes() + let mut signer = new_hmac_sha256(key).map_err(|err| { + debug!("Failed to init SHA256-128 HMAC signer: {}", err); + "Failed to init SHA256-128 HMAC signer" + })?; + signer.sign_oneshot_to_vec(sign_data).map_err(|err| { + debug!("Failed to sign data with SHA256-128 HMAC: {}", err); + "Failed to sign data with SHA256-128 HMAC" + })? }; let dest = &mut data[data_length..]; dest.copy_from_slice(&hash[..signature_length]); @@ -628,16 +657,22 @@ impl Auth { pub fn validate(&self, data: &[u8]) -> bool { match self { - Self::HmacSha256tr128(ref hmac) => { + Self::HmacSha256tr128(ref key) => { let signature_length = self.signature_length(); if data.len() < signature_length { return false; } let received_signature = &data[data.len() - signature_length..]; let data = &data[..data.len() - signature_length]; - let mut hmac = hmac.clone(); - hmac.update(data); - let hash = hmac.finalize().into_bytes(); + let mut signer = if let Ok(signer) = new_hmac_sha256(key) { + signer + } else { + return false; + }; + let mut hash = [0u8; 256 / 8]; + if signer.sign_oneshot(&mut hash, data).is_err() { + return false; + }; &hash[..signature_length] == received_signature } Self::None => true, @@ -653,7 +688,7 @@ impl Auth { } pub struct CryptoStack { - derive_key: Array, + derive_key: Vec, auth_initiator: Auth, auth_responder: Auth, enc_initiator: EncryptionType, @@ -664,12 +699,12 @@ pub struct CryptoStack { impl CryptoStack { fn new(params: &TransformParameters, keys: &DerivedKeys) -> Result { - let mut derive_key = Array::new(keys.derive.len()); - derive_key.data[0..keys.derive.len()].copy_from_slice(&keys.keys[keys.derive.clone()]); + let mut derive_key = vec![0u8; keys.derive.len()]; + derive_key[0..keys.derive.len()].copy_from_slice(&keys.keys[keys.derive.clone()]); let enc = params .enc .as_ref() - .ok_or_else(|| InitError::new("Undefined encryption parameters"))?; + .ok_or_else(|| "Undefined encryption parameters")?; let auth = params .auth .as_ref() @@ -677,7 +712,7 @@ impl CryptoStack { let prf = params .prf .as_ref() - .ok_or_else(|| InitError::new("Undefined pseudorandom transform parameters"))? + .ok_or_else(|| "Undefined pseudorandom transform parameters")? .transform_type; Ok(CryptoStack { derive_key, @@ -738,53 +773,12 @@ impl CryptoStack { self.auth_initiator.validate(data) } - pub fn authenticate_id_initiator(&self, id_initiator: &[u8], dest: &mut [u8]) { - dest.copy_from_slice(self.prf_initiator.prf(id_initiator).as_slice()) - } - - pub fn authenticate_id_responder(&self, id_responder: &[u8], dest: &mut [u8]) { - dest.copy_from_slice(self.prf_responder.prf(id_responder).as_slice()) + pub fn authenticate_id_initiator(&self, id_initiator: &[u8]) -> Result, CryptoError> { + self.prf_initiator.prf(id_initiator) } -} - -struct SliceBuffer<'a> { - slice: &'a mut [u8], - len: usize, -} -impl AsRef<[u8]> for SliceBuffer<'_> { - fn as_ref(&self) -> &[u8] { - &self.slice[..self.len] - } -} - -impl AsMut<[u8]> for SliceBuffer<'_> { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.slice[..self.len] - } -} - -impl Buffer for SliceBuffer<'_> { - fn extend_from_slice(&mut self, other: &[u8]) -> aes_gcm::aead::Result<()> { - if self.len + other.len() <= self.slice.len() { - self.slice[self.len..self.len + other.len()].copy_from_slice(other); - self.len += other.len(); - Ok(()) - } else { - Err(aes_gcm::aead::Error) - } - } - - fn truncate(&mut self, len: usize) { - self.len = len; - } - - fn len(&self) -> usize { - self.len - } - - fn is_empty(&self) -> bool { - self.len == 0 + pub fn authenticate_id_responder(&self, id_responder: &[u8]) -> Result, CryptoError> { + self.prf_responder.prf(id_responder) } } @@ -818,19 +812,19 @@ impl EncryptionType { if transform_type.key_length != Some(256) { return Err("Unsupported key length".into()); } - let cipher = aes::cipher::KeyInit::new_from_slice(key) - .map_err(|_| InitError::new("Failed to init AES CBC 256 cipher"))?; - Ok(Self::AesCbc256(EncryptionAesCbc256 { cipher })) + let mut cipher_key = [0u8; 256 / 8]; + cipher_key.copy_from_slice(key); + Ok(Self::AesCbc256(EncryptionAesCbc256 { cipher_key })) } message::TransformType::ENCR_AES_GCM_16 => { if transform_type.key_length != Some(256) { return Err("Unsupported key length".into()); } - let cipher = aes_gcm::KeyInit::new_from_slice(&key[..32]) - .map_err(|_| InitError::new("Failed to init AES GCM 256 cipher"))?; + let mut cipher_key = [0u8; 32]; + cipher_key.copy_from_slice(&key[..32]); let mut salt = [0u8; 4]; salt.copy_from_slice(&key[32..]); - Ok(Self::AesGcm256(EncryptionAesGcm256 { cipher, salt })) + Ok(Self::AesGcm256(EncryptionAesGcm256 { cipher_key, salt })) } _ => Err("ENC not initialized".into()), } @@ -868,36 +862,46 @@ impl EncryptionType { } } -type AesCbc256Decryptor = cbc::Decryptor; -type AesCbc256Encryptor = cbc::Encryptor; - pub struct EncryptionAesCbc256 { - cipher: Aes256, + cipher_key: [u8; 256 / 8], } impl Encryption for EncryptionAesCbc256 { fn encrypt<'a>(&self, data: &'a mut [u8], msg_len: usize, _: &[u8]) -> Result<(), CryptoError> { - let iv_size = AesCbc256Encryptor::iv_size(); + let aes_cbc_cipher = cipher::Cipher::aes_256_cbc(); + let mut ctx = cipher_ctx::CipherCtx::new().map_err(|err| { + debug!("Failed to init cipher context: {}", err); + "Failed to init cipher context" + })?; + let iv_size = aes_cbc_cipher.iv_length(); let encrypted_payload_length = self.encrypted_payload_length(msg_len); if data.len() < encrypted_payload_length { return Err("Message length is too short".into()); } - let mut iv = Iv::::default(); // Move message to the right to make space for the IV. data.copy_within(..msg_len, iv_size); let padded_msg_len = encrypted_payload_length - iv_size; data[encrypted_payload_length - 1] = (padded_msg_len - 1 - msg_len) as u8; - rand::thread_rng() - .try_fill(iv.as_mut_slice()) + let iv = &mut data[0..iv_size]; + rand::thread_rng().try_fill(iv).map_err(|err| { + debug!("Failed to generate IV for AES CBC 256: {}", err); + "Failed to generate IV for AES CBC 256" + })?; + ctx.encrypt_init(Some(&aes_cbc_cipher), Some(&self.cipher_key), Some(iv)) .map_err(|err| { - debug!("Failed to generate IV for AES CBC 256: {}", err); - "Failed to generate IV for AES CBC 256" + debug!("Failed to init AES CBC 256 encryptor: {}", err); + "Failed to init AES CBC 256 encryptor" })?; - data[..iv_size].copy_from_slice(iv.as_slice()); - let block_encryptor = AesCbc256Encryptor::inner_iv_init(self.cipher.clone(), &iv); - let data_range = &mut data[iv_size..encrypted_payload_length]; - block_encryptor - .encrypt_padded_mut::(data_range, padded_msg_len) + /* + * FIXME: Rust OpenSSL assumes padding is used, so needs one more data block. + * Disabling padding means that data will not overflow, but to bypass an assertion, + * provide a slice larger than is technically needed. + * Even if the signature is overwritten, it shouldn't be a problem, as it's + * checked before decrypting data. + */ + let data_range = &mut data[iv_size..encrypted_payload_length + aes_cbc_cipher.block_size()]; + let _ = ctx + .cipher_update_inplace(data_range, msg_len) .map_err(|err| { debug!("Failed to encode AES CBC 256 message: {}", err); "Failed to encode AES CBC 256 message" @@ -911,38 +915,50 @@ impl Encryption for EncryptionAesCbc256 { msg_len: usize, _: &[u8], ) -> Result<&'a [u8], CryptoError> { - let iv_size = AesCbc256Decryptor::iv_size(); + let aes_cbc_cipher = cipher::Cipher::aes_256_cbc(); + let mut ctx = cipher_ctx::CipherCtx::new().map_err(|err| { + debug!("Failed to init cipher context: {}", err); + "Failed to init cipher context" + })?; + let iv_size = aes_cbc_cipher.iv_length(); if msg_len <= iv_size { return Err("Message length is too short".into()); } - let block_decryptor = - match AesCbc256Decryptor::inner_iv_slice_init(self.cipher.clone(), &data[..iv_size]) { - Ok(dec) => dec, - Err(err) => { - debug!("Failed to init AES CBC 256 IV: {}", err); - return Err("Failed to init AES CBC 256 IV".into()); - } - }; - let data_range = &mut data[iv_size..msg_len]; - match block_decryptor.decrypt_padded_mut::(data_range) { - Ok(data) => Ok(data), - Err(err) => { + let iv = &data[..iv_size]; + ctx.decrypt_init(Some(&aes_cbc_cipher), Some(&self.cipher_key), Some(iv)) + .map_err(|err| { + debug!("Failed to init AES CBC 256 decryptor: {}", err); + "Failed to init AES CBC 256 decryptor" + })?; + ctx.set_padding(false); + /* + * FIXME: Rust OpenSSL assumes padding is used, so needs one more data block. + * Disabling padding means that data will not overflow, but to bypass an assertion, + * provide a slice larger than is technically needed. + * Even if the signature is overwritten, it shouldn't be a problem, as it's + * checked before decrypting data. + */ + let data_range = &mut data[iv_size..msg_len + aes_cbc_cipher.block_size()]; + let _ = ctx + .cipher_update_inplace(data_range, msg_len - iv_size) + .map_err(|err| { debug!("Failed to decode AES CBC 256 message: {}", err); - return Err("Failed to decode AES CBC 256 message".into()); - } - } + "Failed to decode AES CBC 256 message" + })?; + Ok(&data[iv_size..msg_len]) } fn encrypted_payload_length(&self, msg_len: usize) -> usize { - let iv_size = AesCbc256Encryptor::iv_size(); - let block_size = AesCbc256Encryptor::block_size(); + let aes_cbc_cipher = cipher::Cipher::aes_256_cbc(); + let iv_size = aes_cbc_cipher.iv_length(); + let block_size = aes_cbc_cipher.block_size(); let encrypted_size = (1 + msg_len / block_size) * block_size; iv_size + encrypted_size } } pub struct EncryptionAesGcm256 { - cipher: Aes256Gcm, + cipher_key: [u8; 256 / 8], salt: [u8; 4], } @@ -970,21 +986,41 @@ impl Encryption for EncryptionAesGcm256 { "Failed to generate nonce for AES GCM 16 256" })?; data[..8].copy_from_slice(&nonce[4..]); - let mut buffer = SliceBuffer { - slice: &mut data[8..], - len: msg_len, + let aes_gcm_cipher = cipher::Cipher::aes_256_gcm(); + let mut ctx = cipher_ctx::CipherCtx::new().map_err(|err| { + debug!("Failed to init cipher context: {}", err); + "Failed to init cipher context" + })?; + match ctx.encrypt_init(Some(&aes_gcm_cipher), Some(&self.cipher_key), Some(&nonce)) { + Ok(dec) => dec, + Err(err) => { + debug!("Failed to init AES GCM 16 256: {}", err); + return Err("Failed to init AES GCM 16 256".into()); + } }; - let mut cipher = self.cipher.clone(); - cipher - .encrypt_in_place( - Nonce::from_slice(nonce.as_slice()), - associated_data, - &mut buffer, - ) + let _ = ctx.cipher_update(associated_data, None).map_err(|err| { + debug!( + "Failed to process AEAD data in AES GCM 16 256 message: {}", + err + ); + "Failed to process AEAD data in AES GCM 16 256 message" + })?; + let data_range = &mut data[8..8 + msg_len]; + let _ = ctx + .cipher_update_inplace(data_range, msg_len) .map_err(|err| { debug!("Failed to encode AES GCM 16 256 message: {}", err); "Failed to encode AES GCM 16 256 message" })?; + ctx.cipher_final(&mut [0u8; 0]).map_err(|err| { + debug!("Failed to complete AES GCM 16 256 message: {}", err); + "Failed to complete AES GCM 16 256 message" + })?; + ctx.tag(&mut data[8 + msg_len..8 + msg_len + 16]) + .map_err(|err| { + debug!("Failed to write tag for AES GCM 16 256: {}", err); + "Failed to write tag for AES GCM 16 256" + })?; Ok(()) } @@ -994,28 +1030,46 @@ impl Encryption for EncryptionAesGcm256 { msg_len: usize, associated_data: &[u8], ) -> Result<&'a [u8], CryptoError> { - if msg_len <= 8 { + if msg_len <= 16 { return Err("Message length is too short".into()); } let mut nonce = [0u8; 12]; nonce[..4].copy_from_slice(&self.salt); nonce[4..].copy_from_slice(&data[..8]); - let mut buffer = SliceBuffer { - slice: &mut data[8..], - len: msg_len - 8, - }; - let mut cipher = self.cipher.clone(); - match cipher.decrypt_in_place( - Nonce::from_slice(nonce.as_slice()), - associated_data, - &mut buffer, - ) { - Ok(()) => Ok(&buffer.slice[..buffer.len]), - Err(err) => { + let aes_gcm_cipher = cipher::Cipher::aes_256_gcm(); + let mut ctx = cipher_ctx::CipherCtx::new().map_err(|err| { + debug!("Failed to init cipher context: {}", err); + "Failed to init cipher context" + })?; + ctx.decrypt_init(Some(&aes_gcm_cipher), Some(&self.cipher_key), Some(&nonce)) + .map_err(|err| { + debug!("Failed to init AES GCM 16 256: {}", err); + "Failed to init AES GCM 16 256" + })?; + let _ = ctx.cipher_update(associated_data, None).map_err(|err| { + debug!( + "Failed to process AEAD data in AES GCM 16 256 message: {}", + err + ); + "Failed to process AEAD data in AES GCM 16 256 message" + })?; + let tag = &data[msg_len - 16..msg_len]; + ctx.set_tag(tag).map_err(|err| { + debug!("Failed to set tag for AES GCM 16 256: {}", err); + "Failed to set tag for AES GCM 16 256" + })?; + let data_range = &mut data[8..msg_len - 16]; + let bytes_count = ctx + .cipher_update_inplace(data_range, msg_len - 16 - 8) + .map_err(|err| { debug!("Failed to decode AES GCM 16 256 message: {}", err); - return Err("Failed to decode AES GCM 16 256 message".into()); - } - } + "Failed to decode AES GCM 16 256 message" + })?; + ctx.cipher_final(&mut [0u8; 0]).map_err(|err| { + debug!("Failed to validate AES GCM 16 256 message: {}", err); + "Failed to validate AES GCM 16 256 message" + })?; + Ok(&data[8..8 + bytes_count]) } fn encrypted_payload_length(&self, msg_len: usize) -> usize { @@ -1026,6 +1080,21 @@ impl Encryption for EncryptionAesGcm256 { } } +fn new_hmac_sha256( + key: &pkey::PKey, +) -> Result { + sign::Signer::new(hash::MessageDigest::sha256(), &key) +} + +pub fn hash_sha1(data: &[u8]) -> Result<[u8; 160 / 8], CryptoError> { + let mut hash = [0u8; 160 / 8]; + hash::hash_xof(hash::MessageDigest::sha1(), data, &mut hash).map_err(|err| { + debug!("Failed to compute SHA1 hash: {}", err); + "Failed to compute SHA1 hash" + })?; + Ok(hash) +} + pub struct UnsupportedTransform {} impl fmt::Display for UnsupportedTransform { @@ -1051,16 +1120,9 @@ pub struct InitError { msg: &'static str, } -impl InitError { - fn new(msg: &'static str) -> InitError { - InitError { msg } - } -} - impl fmt::Display for InitError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.msg)?; - Ok(()) + f.write_str(self.msg) } } @@ -1072,13 +1134,13 @@ impl fmt::Debug for InitError { impl error::Error for InitError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { - Some(self) + None } } impl From<&'static str> for InitError { fn from(msg: &'static str) -> InitError { - InitError::new(msg) + InitError { msg } } } @@ -1086,16 +1148,9 @@ pub struct CryptoError { msg: &'static str, } -impl CryptoError { - fn new(msg: &'static str) -> CryptoError { - CryptoError { msg } - } -} - impl fmt::Display for CryptoError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.msg)?; - Ok(()) + f.write_str(self.msg) } } @@ -1107,22 +1162,18 @@ impl fmt::Debug for CryptoError { impl error::Error for CryptoError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { - Some(self) + None } } impl From<&'static str> for CryptoError { fn from(msg: &'static str) -> CryptoError { - CryptoError::new(msg) + CryptoError { msg } } } -const DH_MODP_GENERATOR_1024: U1024 = U1024::from_u8(2); -const DH_MODP_RESIDUE_1024: constant_mod::Residue = - const_residue!(DH_MODP_GENERATOR_1024, DHModulus1024); - -impl_modulus!( - DHModulus1024, - U1024, - "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF" -); +const DH_PARAMS_MODP_1024: &str = "-----BEGIN DH PARAMETERS----- +MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR +Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL +/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC +-----END DH PARAMETERS-----"; diff --git a/src/ikev2/mod.rs b/src/ikev2/mod.rs index d3e7e98..e315bcb 100644 --- a/src/ikev2/mod.rs +++ b/src/ikev2/mod.rs @@ -1,6 +1,5 @@ use log::{debug, info, warn}; use rand::Rng; -use sha1::{Digest, Sha1}; use std::{ collections::{self, HashMap}, error, fmt, @@ -691,8 +690,7 @@ impl IKEv2Session { continue; } }; - response - .write_key_exchange_payload(dh.group_number(), public_key.as_slice())?; + response.write_key_exchange_payload(dh.group_number(), public_key)?; } } message::PayloadType::NONCE => { @@ -749,7 +747,14 @@ impl IKEv2Session { // TODO: return INVALID_SYNTAX notification. continue; }; - let skeyseed = prf_transform.prf(shared_secret.as_slice()); + let skeyseed = match prf_transform.prf(shared_secret.as_slice()) { + Ok(skeyseed) => skeyseed, + Err(err) => { + debug!("Failed to create SKEYSEED for keying material {}", err); + // TODO: return INVALID_SYNTAX notification. + continue; + } + }; let prf_transform = match params.create_prf(skeyseed.as_slice()) { Ok(prf) => prf, Err(err) => { @@ -998,11 +1003,17 @@ impl IKEv2Session { initiator_signed [ctx.message_initiator.len()..ctx.message_initiator.len() + ctx.nonce_responder.len()] .copy_from_slice(&ctx.nonce_responder); - crypto_stack.authenticate_id_initiator( - &id_initiator, - &mut initiator_signed - [ctx.message_initiator.len() + ctx.nonce_responder.len()..initiator_signed_len], - ); + match crypto_stack.authenticate_id_initiator(&id_initiator) { + Ok(signature) => initiator_signed + [ctx.message_initiator.len() + ctx.nonce_responder.len()..initiator_signed_len] + .copy_from_slice(&signature), + Err(_) => { + return self.process_auth_failed_response( + response, + "Failed to prepare initiator message to verify signature".into(), + ) + } + } if let Err(err) = client_cert.verify_signature(&initiator_signed[..initiator_signed_len], &client_auth) { @@ -1011,7 +1022,7 @@ impl IKEv2Session { } // At this point the client identity has been successfully verified, proceed with sending a successful response. - self.user_id = Some(client_cert.subject().into()); + self.user_id = client_cert.subject().map(|subject| subject.to_string()); self.state = SessionState::Established; response.start_encrypted_payload()?; if let Some(id_responder) = self.pki_processing.server_id() { @@ -1035,11 +1046,17 @@ impl IKEv2Session { responder_signed[ctx.message_responder.len() ..ctx.message_responder.len() + ctx.nonce_initiator.len()] .copy_from_slice(&ctx.nonce_initiator); - crypto_stack.authenticate_id_responder( - &id_responder, - &mut responder_signed - [ctx.message_responder.len() + ctx.nonce_initiator.len()..responder_signed_len], - ); + match crypto_stack.authenticate_id_responder(&id_responder) { + Ok(signature) => responder_signed + [ctx.message_responder.len() + ctx.nonce_initiator.len()..responder_signed_len] + .copy_from_slice(&signature), + Err(_) => { + return self.process_auth_failed_response( + response, + "Failed to prepare responder message to sign".into(), + ) + } + } let write_slice = response.write_authentication_payload_slice( message::AuthMethod::RSA_DIGITAL_SIGNATURE, @@ -1142,9 +1159,7 @@ fn nat_detection_ip(initiator_spi: u64, responder_spi: u64, addr: IpAddr, port: src_data[16 + addr_len..16 + addr_len + 2].copy_from_slice(&port.to_be_bytes()); let src_data = &src_data[..16 + addr_len + 2]; - let mut hasher = Sha1::new(); - hasher.update(src_data); - hasher.finalize().into() + crypto::hash_sha1(&src_data).unwrap_or([0u8; 20]) } #[derive(Debug)] diff --git a/src/ikev2/pki.rs b/src/ikev2/pki.rs index 7e7e95f..fd57f72 100644 --- a/src/ikev2/pki.rs +++ b/src/ikev2/pki.rs @@ -1,26 +1,10 @@ use std::{error, fmt}; -use der::{oid, Decode, DecodePem, Encode}; use log::debug; -use p256::ecdsa::{self, DerSignature}; -use rsa::{ - pkcs1v15, - pkcs8::{DecodePrivateKey, DecodePublicKey}, - signature::{SignatureEncoding, Signer, Verifier}, - traits::PublicKeyParts, - RsaPrivateKey, RsaPublicKey, -}; -use sha1::{Digest, Sha1}; -use x509_cert::{certificate, Certificate}; +use openssl::{asn1, hash, nid, pkey, sign, x509}; use super::message; -// Simpler than importing the const-oid crate. -const OID_ECDSA_WITH_SHA_256: oid::ObjectIdentifier = - oid::ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2"); -const OID_RSA_ENCRYPTION: oid::ObjectIdentifier = - oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1"); - #[derive(Clone)] pub struct PkiProcessing { server_id: Option>, @@ -101,46 +85,52 @@ impl PkiProcessing { &self, client_cert_der: &[u8], ) -> Result { - let client_cert = Certificate::from_der(client_cert_der)?; - if !certificate_date_valid(&client_cert.tbs_certificate) { + let client_cert = x509::X509::from_der(client_cert_der)?; + if !certificate_date_valid(&client_cert) { return Err("Client certificate has invalid date".into()); } - if !(client_cert - .tbs_certificate - .subject_public_key_info - .algorithm - .oid - == OID_RSA_ENCRYPTION) - { + + if let Some(client_validation) = &self.client_validation { + client_validation.verify_cert(&client_cert)?; + } + let public_key = client_cert.public_key()?; + + if !(public_key.id() == pkey::Id::RSA) { debug!( "Certificate public key algorithm {} is not supported", - client_cert - .tbs_certificate - .subject_public_key_info - .algorithm - .oid + public_key.id().as_raw() ); return Err("Certificate public key algorithm is not supported".into()); } - if let Some(client_validation) = &self.client_validation { - client_validation.verify_cert(&client_cert)?; - } - - let subject = client_cert.tbs_certificate.subject.to_string(); - let public_key_bytes = if let Ok(public_key_bytes) = - client_cert.tbs_certificate.subject_public_key_info.to_der() - { - public_key_bytes + let subject_email = if let Some(sans) = client_cert.subject_alt_names() { + sans.iter() + .filter_map(|san| Some(san.email()?.to_string())) + .next() + } else { + None + }; + let subject_cn = client_cert + .subject_name() + .entries() + .filter_map(|entry| { + if entry.object().nid() == nid::Nid::COMMONNAME { + Some(entry.data().as_utf8().ok()?.to_string()) + } else { + None + } + }) + .next(); + let subject = if subject_email.is_some() { + subject_email + } else if subject_cn.is_some() { + subject_cn } else { - return Err("Certificate has no valid public key".into()); + None }; - let public_key = RsaPublicKey::from_public_key_der(&public_key_bytes)?; - let verifying_key = pkcs1v15::VerifyingKey::new(public_key); - Ok(ClientCertificate { - verifying_key, + public_key, subject, }) } @@ -163,48 +153,44 @@ impl PkiProcessing { } pub struct ClientCertificate { - verifying_key: pkcs1v15::VerifyingKey, - subject: String, + public_key: pkey::PKey, + subject: Option, } impl ClientCertificate { - pub fn subject(&self) -> &str { - self.subject.as_str() + pub fn subject(&self) -> Option<&str> { + self.subject.as_ref().map(|subject| subject.as_str()) } pub fn verify_signature(&self, msg: &[u8], signature: &[u8]) -> Result<(), CertError> { - let signature = pkcs1v15::Signature::try_from(signature)?; - self.verifying_key.verify(msg, &signature)?; - Ok(()) + let mut verifier = sign::Verifier::new(hash::MessageDigest::sha1(), &self.public_key)?; + if !verifier.verify_oneshot(signature, msg)? { + Err("Signature verification failed".into()) + } else { + Ok(()) + } } } #[derive(Clone)] struct ClientValidation { - root_ca: Certificate, + root_ca: x509::X509, root_ca_request: [u8; 20], - public_key: ecdsa::VerifyingKey, + public_key: pkey::PKey, } impl ClientValidation { pub fn new(root_ca_pem: &str) -> Result { - let root_ca = Certificate::from_pem(&root_ca_pem)?; - - let root_ca_bytes = if let Some(root_ca_bytes) = root_ca - .tbs_certificate - .subject_public_key_info - .subject_public_key - .as_bytes() - { - root_ca_bytes - } else { - return Err("Root ca has invalid public key".into()); - }; - let public_key = ecdsa::VerifyingKey::try_from(root_ca_bytes)?; + let root_ca = x509::X509::from_pem(root_ca_pem.as_bytes())?; + + let public_key = root_ca.public_key()?; - let mut hasher = Sha1::new(); - hasher.update(root_ca.tbs_certificate.subject_public_key_info.to_der()?); - let root_ca_request = hasher.finalize().into(); + let mut root_ca_request = [0u8; 20]; + hash::hash_xof( + hash::MessageDigest::sha1(), + public_key.public_key_to_der()?.as_slice(), + &mut root_ca_request, + )?; Ok(ClientValidation { root_ca, @@ -217,38 +203,20 @@ impl ClientValidation { &self.root_ca_request } - fn verify_cert(&self, client_cert: &Certificate) -> Result<(), CertError> { - if client_cert.tbs_certificate.issuer != self.root_ca.tbs_certificate.subject { - // This is probably unnecessary, just a quick check here. - debug!( - "Certificate is signed by {}, expecting {}", - client_cert.tbs_certificate.subject, self.root_ca.tbs_certificate.issuer - ); - return Err("Certificate signed by another issuer".into()); - }; - if !certificate_date_valid(&self.root_ca.tbs_certificate) { + fn verify_cert(&self, client_cert: &x509::X509) -> Result<(), CertError> { + if !certificate_date_valid(&self.root_ca) { return Err("Root CA certificate has invalid date".into()); } - let signed_data = client_cert.tbs_certificate.to_der()?; - let signature = match client_cert.signature.as_bytes() { - Some(signature) => signature, - None => return Err("Client cert has no valid signature".into()), - }; - if !(client_cert.signature_algorithm.oid == OID_ECDSA_WITH_SHA_256) { - debug!( - "Certificate signature algorithm {} is not supported", - client_cert.signature_algorithm.oid - ); - return Err("Certificate signature algorithm is not supported".into()); - }; - let signature = DerSignature::try_from(signature)?; - self.public_key.verify(&signed_data, &signature)?; + if !client_cert.verify(&self.public_key)? { + return Err("Certificate verification failed".into()); + } + if self.root_ca.issued(client_cert) != x509::X509VerifyResult::OK { + return Err("Certificate was not issued by the root CA".into()); + } /* TODO: check the following extensions: - - Subject Alternative Name (OID 2.5.29.17) + - Subject Alternative Name (OID 2.5.29.17) (matches root CA limitations) - Enhanced Key Usage (OID 2.5.29.37) - Each needs a custom DecodeValue trait/implementation for the der crate; - most likely with a #[derive(Choice)] annotation. */ Ok(()) @@ -259,22 +227,21 @@ impl ClientValidation { struct ServerIdentity { public_cert_der: Vec, signature_length: usize, - signing_key: pkcs1v15::SigningKey, + private_key: pkey::PKey, } impl ServerIdentity { fn new(public_cert_pem: &str, private_key_pem: &str) -> Result { // TODO: if client requests cert, check if public key is signed by CA from client's CERTREQUEST. - let public_cert_der = Certificate::from_pem(&public_cert_pem)?; + let public_cert_der = x509::X509::from_pem(public_cert_pem.as_bytes())?; let public_cert_der = public_cert_der.to_der()?; - let private_key = RsaPrivateKey::from_pkcs8_pem(&private_key_pem)?; + let private_key = pkey::PKey::private_key_from_pem(private_key_pem.as_bytes())?; let signature_length = private_key.size(); - let signing_key = pkcs1v15::SigningKey::::new(private_key); Ok(ServerIdentity { public_cert_der, signature_length, - signing_key, + private_key, }) } @@ -283,37 +250,34 @@ impl ServerIdentity { } fn sign_message(&self, msg: &[u8], dest: &mut [u8]) -> Result<(), CertError> { - let signature = self.signing_key.try_sign(msg)?; - dest.copy_from_slice(&signature.to_bytes()); + let mut signer = sign::Signer::new(hash::MessageDigest::sha1(), &self.private_key)?; + signer.sign_oneshot(dest, msg)?; Ok(()) } } -fn certificate_date_valid(cert: &certificate::TbsCertificateInner) -> bool { - let time_now = std::time::SystemTime::now(); - cert.validity.not_before.to_system_time() < time_now - && cert.validity.not_after.to_system_time() > time_now +fn certificate_date_valid(cert: &x509::X509) -> bool { + let time_now = match asn1::Asn1Time::days_from_now(0) { + Ok(time) => time, + Err(err) => { + debug!("Failed to get current time in ASN format: {}", err); + return false; + } + }; + cert.not_before() < time_now && cert.not_after() > time_now } #[derive(Debug)] pub enum CertError { Internal(&'static str), - Pkcs8(rsa::pkcs8::Error), - Rsa(rsa::Error), - Der(der::Error), - Ecdsa(p256::ecdsa::Error), - Spki(x509_cert::spki::Error), + OpenSSL(openssl::error::ErrorStack), } impl fmt::Display for CertError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Self::Internal(msg) => f.write_str(msg), - Self::Pkcs8(ref e) => write!(f, "Decode error: {}", e), - Self::Rsa(ref e) => write!(f, "Signature verification error: {}", e), - Self::Der(ref e) => write!(f, "Decode error: {}", e), - Self::Ecdsa(ref e) => write!(f, "Decode error: {}", e), - Self::Spki(ref e) => write!(f, "Decode error: {}", e), + Self::OpenSSL(ref e) => write!(f, "OpenSSL error: {}", e), } } } @@ -322,11 +286,7 @@ impl error::Error for CertError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match *self { Self::Internal(_msg) => None, - Self::Pkcs8(ref err) => Some(err), - Self::Rsa(ref err) => Some(err), - Self::Der(ref err) => Some(err), - Self::Ecdsa(ref err) => Some(err), - Self::Spki(ref err) => Some(err), + Self::OpenSSL(ref err) => Some(err), } } } @@ -337,32 +297,8 @@ impl From<&'static str> for CertError { } } -impl From for CertError { - fn from(err: rsa::pkcs8::Error) -> CertError { - Self::Pkcs8(err) - } -} - -impl From for CertError { - fn from(err: rsa::Error) -> CertError { - Self::Rsa(err) - } -} - -impl From for CertError { - fn from(err: der::Error) -> CertError { - Self::Der(err) - } -} - -impl From for CertError { - fn from(err: p256::ecdsa::Error) -> CertError { - Self::Ecdsa(err) - } -} - -impl From for CertError { - fn from(err: x509_cert::spki::Error) -> CertError { - Self::Spki(err) +impl From for CertError { + fn from(err: openssl::error::ErrorStack) -> CertError { + Self::OpenSSL(err) } }