feat: OpenSSL crypto support for kms/crate/utils with full FIPS support (#115)

* Rebase grnx-fix-fips from develop

* Pull Cargo.lock from develop

* fips: fixed test_password_wrap_import and opened issue #124

* fips: generated non-deprecated pkcs12 certificates

* test: fix old uid size in test covercrypt import key

* fix: revert some unneeded changes

* fixbug: padded be bytes key conversions

* fix: typos

* ci: rewrite tests in container

* Revert "ci: rewrite tests in container"

This reverts commit 7adb273660.

* ci: redis hostname 0.0.0.0

* docs: wrote docs on fips algorithms

* ci: use host addresses for docker containers use

* ci: merge ubuntu workflows

* fixbug: padded remnant conversions with âd_be_bytes() on other files

* fix: given mutability in function call in pad_be_bytes()

* ci: re-add test var. env. for Google CSE

* ci: re-add test var. env. for Google CSE

* bugfix: to_u32_digits() created little-endian u32 from big endian bytes.

* fix: Remove bootstrap server and enclave (#122)

* Remove bootstrap server

* Remove enclave-related stuff

* Apply suggestions from code review

Co-authored-by: heavenboy <49846064+heavenboy8@users.noreply.github.com>

* Update crate/server/README.md

Co-authored-by: heavenboy <49846064+heavenboy8@users.noreply.github.com>

* Renaming verify_cert into client_cert

* Add KMS supervisor conf and instructions

* remove acme dep

---------

Co-authored-by: heavenboy <49846064+heavenboy8@users.noreply.github.com>
Co-authored-by: Sébastien Lambert <sebastien.lambert@cosmian.com>

* comments: added hunk of comments on bugfix for to_u32_digits()

* fix: remove useless deps from Cargo.toml

* bugfix: padded in kmip_data_structure an EC private key from BigUint to bytes

* fix: remove openssl from cli

---------

Co-authored-by: Bruno Grieder <bruno.grieder@cosmian.com>
Co-authored-by: Manuthor <manu.coste@gmail.com>
Co-authored-by: Thibs <ThibsG@users.noreply.github.com>
Co-authored-by: heavenboy <49846064+heavenboy8@users.noreply.github.com>
Co-authored-by: Sébastien Lambert <sebastien.lambert@cosmian.com>
This commit is contained in:
JosePisco 2023-12-21 12:07:11 +01:00 committed by GitHub
parent 36bd22ef97
commit 3ad1bc7217
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
136 changed files with 2722 additions and 5058 deletions

130
.github/workflows/build_all.yml vendored Normal file
View file

@ -0,0 +1,130 @@
---
name: Build all
on:
workflow_call:
inputs:
toolchain:
required: true
type: string
env:
OPENSSL_DIR: /tmp/openssl_fips
jobs:
centos7:
uses: ./.github/workflows/build_centos7.yml
secrets: inherit
with:
toolchain: ${{ inputs.toolchain }}
archive-name: centos7
commands: |
cargo build --release --bin ckms
cargo build --release --bin cosmian_kms_server
cargo test --release -- --nocapture
cargo test --release --bins -- --nocapture
artifacts: |
target/release/ckms
target/release/cosmian_kms_server
fips-centos7:
uses: ./.github/workflows/build_centos7.yml
secrets: inherit
with:
toolchain: ${{ inputs.toolchain }}
archive-name: fips_centos7
commands: |
cargo build --release --features fips --bin ckms
cargo build --release --features fips --bin cosmian_kms_server
cargo test --release --features fips -- --nocapture
cargo test --release --features fips --bins -- --nocapture
artifacts: |
target/release/ckms
target/release/cosmian_kms_server
ubuntu-20:
uses: ./.github/workflows/build_generic_linux.yml
secrets: inherit
with:
toolchain: ${{ inputs.toolchain }}
distribution: ubuntu-20.04
archive-name: ubuntu_20_04
commands: |
cargo build --release --bin ckms
cargo build --release --bin cosmian_kms_server
cargo test --release -- --nocapture --skip test_mysql --skip test_pgsql --skip test_redis
cargo test --release --bins -- --nocapture --skip test_mysql --skip test_pgsql --skip test_redis
artifacts: |
target/release/ckms
target/release/cosmian_kms_server
fips-ubuntu-20:
uses: ./.github/workflows/build_generic_linux.yml
secrets: inherit
with:
toolchain: ${{ inputs.toolchain }}
distribution: ubuntu-20.04
archive-name: fips_ubuntu_20_04
commands: |
cargo build --release --features fips --bin ckms
cargo build --release --features fips --bin cosmian_kms_server
cargo test --release --features fips -- --nocapture --skip test_mysql --skip test_pgsql --skip test_redis
cargo test --release --features fips --bins -- --nocapture --skip test_mysql --skip test_pgsql --skip test_redis
artifacts: |
target/release/ckms
target/release/cosmian_kms_server
ubuntu-22:
uses: ./.github/workflows/build_generic_linux.yml
secrets: inherit
with:
toolchain: ${{ inputs.toolchain }}
distribution: ubuntu-22.04
archive-name: ubuntu_22_04
commands: |
cargo build --release --bin ckms
cargo build --release --bin cosmian_kms_server
cargo test --release -- --nocapture --skip test_mysql --skip test_pgsql --skip test_redis
cargo test --release --bins -- --nocapture --skip test_mysql --skip test_pgsql --skip test_redis
artifacts: |
target/release/ckms
target/release/cosmian_kms_server
windows:
uses: ./.github/workflows/build_generic_linux.yml
with:
toolchain: ${{ inputs.toolchain }}
distribution: ubuntu-20.04
archive-name: windows
commands: |
sudo apt-get install --no-install-recommends -qq libclang-dev gcc-mingw-w64-x86-64
rustup target add x86_64-pc-windows-gnu
rm -rf /tmp/openssl_fips
mkdir -p /tmp/openssl_fips
bash ./scripts/local_ossl_instl.sh /tmp/openssl_fips cross-compile-windows
cargo build --release --bin ckms --target x86_64-pc-windows-gnu
cargo build --release --bin cosmian_kms_server --target x86_64-pc-windows-gnu
artifacts: |
target/x86_64-pc-windows-gnu/release/ckms.exe
target/x86_64-pc-windows-gnu/release/cosmian_kms_server.exe
mac:
uses: ./.github/workflows/build_generic_linux.yml
with:
toolchain: ${{ inputs.toolchain }}
distribution: macos-12
archive-name: macos
commands: |
rustup target add x86_64-apple-darwin
cargo build --release --bin ckms --target x86_64-apple-darwin
cargo build --release --bin cosmian_kms_server --target x86_64-apple-darwin
artifacts: |
target/x86_64-apple-darwin/release/ckms
target/x86_64-apple-darwin/release/cosmian_kms_server

148
.github/workflows/build_centos7.yml vendored Normal file
View file

@ -0,0 +1,148 @@
---
name: Centos
on:
workflow_call:
inputs:
toolchain:
required: true
type: string
commands:
required: true
type: string
archive-name:
required: true
type: string
artifacts:
required: true
type: string
env:
OPENSSL_DIR: /tmp/openssl_fips
jobs:
centos7-tests:
services:
# Label used to access the service container
postgres:
# Docker Hub image
image: postgres
# Provide the password for postgres
env:
POSTGRES_USER: kms
PGUSER: kms
POSTGRES_PASSWORD: kms
POSTGRES_DB: kms
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
mariadb:
image: mariadb
env:
MYSQL_DATABASE: kms
MYSQL_ROOT_PASSWORD: kms
redis:
image: redis
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
runs-on: ubuntu-22.04
name: ${{ inputs.archive-name }}
container:
image: iyxd/centos7-rust
steps:
- name: Display cpuinfo
run: cat /proc/cpuinfo
- uses: actions/checkout@v3
# `curl` in Centos 7 is too old (7.29) regarding
# what `dtolnay/rust-toolchain` needs because it is
# using `--retry-connrefused` option (curl 7.52).
# Drop this when moving to Centos 8
- name: Update curl for Centos 7
run: |
curl --version
chmod +x ./scripts/centos_install_curl.sh
./scripts/centos_install_curl.sh
curl --version
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ inputs.toolchain }}
components: rustfmt, clippy
- name: Centos 7 prerequisites
run: |
set -x
export PERL_MM_USE_DEFAULT=1
yum -y groupinstall "Development Tools"
yum -y install perl-IPC-Cmd perl-Digest-SHA1 perl-CPAN perl-devel
- name: install cpanm and SHA module
uses: perl-actions/install-with-cpanm@stable
with:
install: |
Digest::SHA
sudo: false
- name: Local OpenSSL FIPS Install
run: |
bash ./scripts/local_ossl_instl.sh ${{ env.OPENSSL_DIR }}
- name: Pre-cleanup
run: |
set -x
rm -rf /tmp/sqlite-data /tmp/tmp.json
- name: Build and tests in release
run: |
set -x
${{ inputs.commands }}
env:
OPENSSL_DIR: ${{ env.OPENSSL_DIR }}
RUST_LOG: cosmian_kms_server=trace
POSTGRES_USER: kms
PGUSER: kms
POSTGRES_PASSWORD: kms
POSTGRES_DB: kms
KMS_POSTGRES_URL: postgres://kms:kms@postgres/kms
MYSQL_DATABASE: kms
MYSQL_ROOT_PASSWORD: kms
KMS_MYSQL_URL: mysql://root:kms@mariadb/kms
KMS_ENCLAVE_DIR_PATH: data/public
KMS_SQLITE_PATH: data/shared
KMS_CERTBOT_SSL_PATH: data/private
REDIS_HOST: redis
# Google variables
TEST_GOOGLE_OAUTH_CLIENT_ID: ${{ secrets.TEST_GOOGLE_OAUTH_CLIENT_ID }}
TEST_GOOGLE_OAUTH_CLIENT_SECRET: ${{ secrets.TEST_GOOGLE_OAUTH_CLIENT_SECRET }}
TEST_GOOGLE_OAUTH_REFRESH_TOKEN: ${{ secrets.TEST_GOOGLE_OAUTH_REFRESH_TOKEN }}
# Speeds up Ristretto 25519 multiplication x 2
RUSTFLAGS: --cfg curve25519_dalek_backend="simd" -C target-cpu=native
- name: Upload KMS for Centos7
uses: actions/upload-artifact@v3
with:
name: ${{ inputs.archive-name }}
path: ${{ inputs.artifacts }}
retention-days: 1
if-no-files-found: error

View file

@ -10,7 +10,7 @@ env:
jobs:
pyo3:
uses: ./.github/workflows/python_build.yml
uses: ./.github/workflows/maturin.yml
with:
toolchain: nightly-2023-08-17

View file

@ -1,5 +1,5 @@
---
name: KMS build
name: KMS build for MacOS and Windows runner - no docker
on:
workflow_call:
@ -20,9 +20,12 @@ on:
required: true
type: string
env:
OPENSSL_DIR: /tmp/openssl_fips
jobs:
kms-build:
name: Build ${{ inputs.archive-name }}
name: ${{ inputs.archive-name }}
runs-on: ${{ inputs.distribution }}
steps:
- name: Display cpuinfo
@ -51,12 +54,23 @@ jobs:
toolchain: ${{ inputs.toolchain }}
components: rustfmt, clippy
- name: Local OpenSSL FIPS Install
if: steps.cargo_cache.outputs.cache-hit != 'true'
run: |
bash ./scripts/local_ossl_instl.sh ${{ env.OPENSSL_DIR }}
- name: Build
if: steps.cargo_cache.outputs.cache-hit != 'true'
run: ${{ inputs.commands }}
env:
# Speeds up Ristretto 25519 multiplication x 2
RUSTFLAGS: --cfg curve25519_dalek_backend="simd" -C target-cpu=native
OPENSSL_DIR: ${{ env.OPENSSL_DIR }}
# Google variables
TEST_GOOGLE_OAUTH_CLIENT_ID: ${{ secrets.TEST_GOOGLE_OAUTH_CLIENT_ID }}
TEST_GOOGLE_OAUTH_CLIENT_SECRET: ${{ secrets.TEST_GOOGLE_OAUTH_CLIENT_SECRET }}
TEST_GOOGLE_OAUTH_REFRESH_TOKEN: ${{ secrets.TEST_GOOGLE_OAUTH_REFRESH_TOKEN }}
- name: Upload KMS for ${{ inputs.distribution }}
if: steps.cargo_cache.outputs.cache-hit != 'true'

View file

@ -1,224 +0,0 @@
---
name: Cargo tests
on:
workflow_call:
inputs:
toolchain:
required: true
type: string
jobs:
# Cleanup task is required for self-hosted runner since docker user is root and all files in target folder are under root permissions
cleanup:
runs-on: self-hosted
steps:
- name: Chown user
run: |
echo "USER: $USER"
echo "GITHUB_WORKSPACE: $GITHUB_WORKSPACE"
sudo chown -R $USER:$USER $GITHUB_WORKSPACE
kms-tests:
needs: cleanup
services:
# Label used to access the service container
postgres:
# Docker Hub image
image: postgres
# Provide the password for postgres
env:
POSTGRES_USER: kms
PGUSER: kms
POSTGRES_PASSWORD: kms
POSTGRES_DB: kms
# Set health checks to wait until postgres has started
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
mariadb:
image: mariadb
env:
MYSQL_DATABASE: kms
MYSQL_ROOT_PASSWORD: kms
redis:
image: redis
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
runs-on: ubuntu-22.04
# runs-on: [self-hosted, not-sgx] # currently not needed.
container:
image: iyxd/centos7-rust
steps:
- name: Display cpuinfo
run: cat /proc/cpuinfo
- uses: actions/checkout@v3
# `curl` in Centos 7 is too old (7.29) regarding
# what `dtolnay/rust-toolchain` needs because it is
# using `--retry-connrefused` option (curl 7.52).
# Drop this when moving to Centos 8
- name: Update curl for Centos 7
run: |
curl --version
chmod +x ./scripts/centos_install_curl.sh
./scripts/centos_install_curl.sh
curl --version
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ inputs.toolchain }}
components: rustfmt, clippy
- name: Centos 7 prerequisites
run: |
yum -y groupinstall "Development Tools"
yum -y install openssl openssl-devel
- name: Tests in debug
run: |
rm -rf /tmp/sqlite-data /tmp/tmp.json
export RUST_BACKTRACE=1 CARGO_INCREMENTAL=0
cargo build --no-default-features
cargo test --no-default-features -- --nocapture
cargo test --bins -- --nocapture
env:
POSTGRES_USER: kms
PGUSER: kms
POSTGRES_PASSWORD: kms
POSTGRES_DB: kms
KMS_POSTGRES_URL: postgres://kms:kms@postgres/kms
MYSQL_DATABASE: kms
MYSQL_ROOT_PASSWORD: kms
KMS_MYSQL_URL: mysql://root:kms@mariadb/kms
KMS_ENCLAVE_DIR_PATH: data/public
KMS_SQLITE_PATH: data/shared
KMS_CERTBOT_SSL_PATH: data/private
REDIS_HOST: redis
TEST_GOOGLE_OAUTH_CLIENT_ID: ${{ secrets.TEST_GOOGLE_OAUTH_CLIENT_ID }}
TEST_GOOGLE_OAUTH_CLIENT_SECRET: ${{ secrets.TEST_GOOGLE_OAUTH_CLIENT_SECRET }}
TEST_GOOGLE_OAUTH_REFRESH_TOKEN: ${{ secrets.TEST_GOOGLE_OAUTH_REFRESH_TOKEN }}
kms-centos7:
needs: cleanup
runs-on: ubuntu-22.04
# runs-on: [self-hosted, not-sgx] # currently not needed.
container:
image: iyxd/centos7-rust
steps:
- uses: actions/checkout@v3
# `curl` in Centos 7 is too old (7.29) regarding
# what `dtolnay/rust-toolchain` needs because it is
# using `--retry-connrefused` option (curl 7.52).
# Drop this when moving to Centos 8
- name: Update curl for Centos 7
run: |
curl --version
chmod +x ./scripts/centos_install_curl.sh
./scripts/centos_install_curl.sh
curl --version
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ inputs.toolchain }}
components: rustfmt, clippy
- name: Centos 7 prerequisites
run: |
yum -y groupinstall "Development Tools"
yum -y install openssl openssl-devel
- name: Build with Auth0 feature
run: |
cargo build --release --bin ckms
cargo build --release --bin cosmian_kms_server
env:
# Speeds up Ristretto 25519 multiplication x 2
RUSTFLAGS: --cfg curve25519_dalek_backend="simd" -C target-cpu=native
- name: Upload KMS for Centos7
uses: actions/upload-artifact@v3
with:
name: kms_centos7
path: |
target/release/ckms
target/release/cosmian_kms_server
retention-days: 1
if-no-files-found: error
kms-ubuntu-20:
needs: cleanup
uses: ./.github/workflows/cargo_build.yml
with:
toolchain: ${{ inputs.toolchain }}
distribution: ubuntu-20.04
archive-name: kms_ubuntu_20_04
commands: |
cargo build --release --bin ckms
cargo build --release --bin cosmian_kms_server
artifacts: |
target/release/ckms
target/release/cosmian_kms_server
kms-ubuntu-22:
needs: cleanup
uses: ./.github/workflows/cargo_build.yml
with:
toolchain: ${{ inputs.toolchain }}
distribution: ubuntu-22.04
archive-name: kms_ubuntu_22_04
commands: |
cargo build --release --bin ckms
cargo build --release --bin cosmian_kms_server
artifacts: |
target/release/ckms
target/release/cosmian_kms_server
kms-windows:
needs: cleanup
uses: ./.github/workflows/cargo_build.yml
with:
toolchain: ${{ inputs.toolchain }}
distribution: ubuntu-20.04
archive-name: kms_windows
commands: |
sudo apt-get install --no-install-recommends -qq libclang-dev gcc-mingw-w64-x86-64
rustup target add x86_64-pc-windows-gnu
cargo build --release --bin ckms --target x86_64-pc-windows-gnu
cargo build --release --bin cosmian_kms_server --target x86_64-pc-windows-gnu
artifacts: |
target/x86_64-pc-windows-gnu/release/ckms.exe
target/x86_64-pc-windows-gnu/release/cosmian_kms_server.exe
kms-mac:
needs: cleanup
uses: ./.github/workflows/cargo_build.yml
with:
toolchain: ${{ inputs.toolchain }}
distribution: macos-12
archive-name: kms_macos
commands: |
rustup target add x86_64-apple-darwin
cargo build --release --bin ckms --target x86_64-apple-darwin
cargo build --release --bin cosmian_kms_server --target x86_64-apple-darwin
artifacts: |
target/x86_64-apple-darwin/release/ckms
target/x86_64-apple-darwin/release/cosmian_kms_server

View file

@ -8,6 +8,9 @@ on:
required: true
type: string
env:
OPENSSL_DIR: /tmp/openssl_fips
jobs:
lint:
runs-on: ubuntu-22.04
@ -28,6 +31,10 @@ jobs:
key: ${{ runner.os }}-cargo-lint-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-lint-${{ hashFiles('**/Cargo.lock') }}
- name: Local OpenSSL FIPS Install
run: |
bash ./scripts/local_ossl_instl.sh ${{ env.OPENSSL_DIR }}
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ inputs.toolchain }}
@ -39,3 +46,5 @@ jobs:
- name: Static analysis
run: cargo clippy --workspace --all-targets --all-features --release -- -D warnings
env:
OPENSSL_DIR: ${{ env.OPENSSL_DIR }}

View file

@ -1,123 +0,0 @@
---
name: Docker KMS SGX
on:
workflow_call:
env:
REGISTRY: ghcr.io
REGISTRY_IMAGE: ghcr.io/cosmian/enclave-kms-insecure
KMS_USE_BOOTSTRAP_SERVER: true
KMS_USE_CERTBOT: true
KMS_CERTBOT_HOSTNAME: kms.sgx.ci.cosmian.dev
KMS_CERTBOT_EMAIL: tech@cosmian.com
KMS_CERTBOT_USE_TEE_KEY: abcdef0123456789
TESTS_CONTAINER_NETWORK: # dynamic variable retrieved during the tests
jobs:
build-push-image-and-tests:
runs-on: [self-hosted, sgx]
container:
image: ubuntu:22.04
ports:
- 4431:443
- 8081:80
steps:
- name: Install tools
run: |
apt update
apt install --no-install-recommends -qq -y \
docker.io \
curl \
ca-certificates \
build-essential
- uses: actions/checkout@v3
# Following job from `docker_kms_sgx_tests.yml` will overwrite
# this file, but for unknown reason sometimes permissions are denied to do so.
- name: Fix permissions on JSON file
run: chmod o+w ./ci/sgx/kms-test-ci.json
- name: Login to GitHub Packages
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY_IMAGE }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# Specify staging features to use Let's Encrypt staging environment in order to be able to request more than 5 certificates by week
# Documentation : https://letsencrypt.org/docs/duplicate-certificate-limit/
- name: Build and tag docker container
uses: docker/build-push-action@v3
with:
file: ./ci/sgx/Dockerfile.sgx
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
network: host
build-args: |
FEATURES=--features staging
#########
# Tests #
#########
- name: Retrieve current Docker network
run: echo "TESTS_CONTAINER_NETWORK=$(docker network ls |grep github_network | head -n1 | cut -d' ' -f1)" >> $GITHUB_ENV
- name: Docker start container
run: |
docker run \
--pull always \
--network "${{ env.TESTS_CONTAINER_NETWORK }}" \
--device /dev/sgx_enclave \
--device /dev/sgx_provision \
-e KMS_USE_BOOTSTRAP_SERVER="${{ env.KMS_USE_BOOTSTRAP_SERVER }}" \
-e KMS_USE_CERTBOT="${{ env.KMS_USE_CERTBOT }}" \
-e KMS_CERTBOT_HOSTNAME="${{ env.KMS_CERTBOT_HOSTNAME }}" \
-e KMS_CERTBOT_EMAIL="${{ env.KMS_CERTBOT_EMAIL }}" \
-e KMS_CERTBOT_USE_TEE_KEY="${{ env.KMS_CERTBOT_USE_TEE_KEY }}" \
-v /var/run/aesmd:/var/run/aesmd/ \
-v /opt/cosmian-internal:/opt/cosmian-internal \
-p 80:80 \
-p 9998:9998 \
-d --rm --name enclave-kms-insecure ghcr.io/cosmian/enclave-kms-insecure:${{ steps.meta.outputs.version }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install ckms
run: cargo install --locked --path crate/cli
- name: Bootstrap
run: |
KMS_CLI_CONF=$PWD/ci/sgx/kms-test-ci.json ckms bootstrap-start --sqlite-path private_data/ --database-type sqlite
sleep 20
- name: Verify
run: KMS_CLI_CONF=$PWD/ci/sgx/kms-test-ci.json ckms verify
- name: Symmetric key create
run: KMS_CLI_CONF=$PWD/ci/sgx/kms-test-ci.json ckms sym keys create
- name: Docker stop container
if: success() || failure()
run: |
docker logs --tail 20 enclave-kms-insecure
docker stop enclave-kms-insecure

View file

@ -0,0 +1,9 @@
---
name: Github cache cleanup
on: workflow_dispatch
jobs:
cleanup:
uses: Cosmian/reusable_workflows/.github/workflows/cleanup_cache.yml@develop
secrets: inherit

View file

@ -22,12 +22,12 @@ jobs:
uses: Cosmian/reusable_workflows/.github/workflows/cargo-udeps.yml@develop
cargo-lint:
uses: ./.github/workflows/lint.yml
uses: ./.github/workflows/clippy.yml
with:
toolchain: nightly-2023-08-17
cargo-tests:
uses: ./.github/workflows/cargo_tests.yml
build:
uses: ./.github/workflows/build_all.yml
secrets: inherit
with:
toolchain: nightly-2023-08-17
@ -38,7 +38,7 @@ jobs:
toolchain: nightly-2023-08-17
python_and_docker:
uses: ./.github/workflows/python_and_docker.yml
uses: ./.github/workflows/build_docker_image.yml
##############################################################################
### Releases
@ -50,9 +50,8 @@ jobs:
- cargo-udeps
- cargo-lint
- cargo-doc
- cargo-tests
- build
- python_and_docker
# - sgx_docker # do not depend on this for releases
runs-on: [self-hosted, not-sgx]
strategy:
fail-fast: false

1
.gitignore vendored
View file

@ -10,7 +10,6 @@ data/keys
**/data/public
**/data/shared
crate/cli/*.sqlite
sgx/kms-test-*.json
.DS_Store
.idea/
rustc-ice*.txt

View file

@ -3,8 +3,6 @@
"actix",
"ascii",
"canonicalize",
"certbot",
"Certbot",
"chacha",
"ciphertext",
"ciphertexts",
@ -26,7 +24,6 @@
"keypair",
"kmip",
"KMIP",
"libsgx",
"mozilla",
"mysql",
"nginx",
@ -68,5 +65,6 @@
"rust-analyzer.check.command": "clippy",
"[python]": {
"editor.defaultFormatter": "ms-python.autopep8"
}
},
"rust-analyzer.showUnlinkedFileNotification": false
}

435
Cargo.lock generated
View file

@ -2,21 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "acme-lib"
version = "0.9.0"
source = "git+https://github.com/Cosmian/acme-lib?branch=master#12e2831797394778ff1136216a459fb3a2f6087f"
dependencies = [
"base64 0.21.5",
"lazy_static",
"log",
"openssl",
"serde",
"serde_json",
"time 0.1.45",
"ureq",
]
[[package]]
name = "actix-codec"
version = "0.5.1"
@ -49,29 +34,6 @@ dependencies = [
"smallvec",
]
[[package]]
name = "actix-files"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d832782fac6ca7369a70c9ee9a20554623c5e51c76e190ad151780ebea1cf689"
dependencies = [
"actix-http",
"actix-service",
"actix-utils",
"actix-web",
"askama_escape",
"bitflags 1.3.2",
"bytes",
"derive_more",
"futures-core",
"http-range",
"log",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite",
]
[[package]]
name = "actix-http"
version = "3.4.0"
@ -138,44 +100,6 @@ dependencies = [
"syn 2.0.39",
]
[[package]]
name = "actix-multipart"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b960e2aea75f49c8f069108063d12a48d329fc8b60b786dfc7552a9d5918d2d"
dependencies = [
"actix-multipart-derive",
"actix-utils",
"actix-web",
"bytes",
"derive_more",
"futures-core",
"futures-util",
"httparse",
"local-waker",
"log",
"memchr",
"mime",
"serde",
"serde_json",
"serde_plain",
"tempfile",
"tokio",
]
[[package]]
name = "actix-multipart-derive"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a0a77f836d869f700e5b47ac7c3c8b9c8bc82e4aec861954c6198abee3ebd4d"
dependencies = [
"darling",
"parse-size",
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "actix-router"
version = "0.5.1"
@ -313,7 +237,7 @@ dependencies = [
"serde_urlencoded",
"smallvec",
"socket2 0.5.5",
"time 0.3.30",
"time",
"url",
]
@ -501,21 +425,6 @@ dependencies = [
"password-hash",
]
[[package]]
name = "askama_escape"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
[[package]]
name = "asn1"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae3ecbce89a22627b5e8e6e11d69715617138290289e385cde773b1fe50befdb"
dependencies = [
"asn1_derive",
]
[[package]]
name = "asn1-rs"
version = "0.5.2"
@ -529,7 +438,7 @@ dependencies = [
"num-traits",
"rusticata-macros",
"thiserror",
"time 0.3.30",
"time",
]
[[package]]
@ -555,17 +464,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "asn1_derive"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "861af988fac460ac69a09f41e6217a8fb9178797b76fcc9478444be6a59be19c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "assert_cmd"
version = "2.0.12"
@ -649,6 +547,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base58"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581"
[[package]]
name = "base64"
version = "0.13.1"
@ -673,21 +577,6 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[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"
@ -994,12 +883,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "codicon"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12170080f3533d6f09a19f81596f836854d0fa4867dc32c8172b8474b4e9de61"
[[package]]
name = "combine"
version = "4.6.6"
@ -1043,7 +926,7 @@ dependencies = [
"rand",
"sha2",
"subtle",
"time 0.3.30",
"time",
"version_check",
]
@ -1147,6 +1030,7 @@ dependencies = [
name = "cosmian_kmip"
version = "4.10.1"
dependencies = [
"base58",
"bitflags 2.4.1",
"chrono",
"cloudproof",
@ -1157,9 +1041,10 @@ dependencies = [
"pyo3",
"serde",
"serde_json",
"sha3",
"strum",
"thiserror",
"time 0.3.30",
"time",
"tracing",
"uuid",
"zeroize",
@ -1183,20 +1068,17 @@ dependencies = [
"cosmian_kms_utils",
"cosmian_logger",
"der",
"hex",
"oauth2",
"openssl",
"pem",
"predicates",
"rand",
"ratls",
"regex",
"reqwest",
"rustls",
"serde",
"serde_json",
"strum",
"tee_attestation",
"tempfile",
"thiserror",
"tokio",
@ -1217,16 +1099,11 @@ dependencies = [
"http",
"josekit",
"log",
"openssl",
"ratls",
"reqwest",
"rustls",
"serde",
"serde_json",
"tee_attestation",
"thiserror",
"tokio",
"tokio-util",
"url",
"webpki-roots 0.22.6",
]
@ -1251,12 +1128,9 @@ dependencies = [
name = "cosmian_kms_server"
version = "4.10.1"
dependencies = [
"acme-lib",
"actix-cors",
"actix-files",
"actix-http",
"actix-identity",
"actix-multipart",
"actix-rt",
"actix-service",
"actix-tls",
@ -1281,16 +1155,14 @@ dependencies = [
"lazy_static",
"mime",
"openssl",
"ratls",
"rawsql",
"redis",
"reqwest",
"serde",
"serde_json",
"sqlx",
"tee_attestation",
"thiserror",
"time 0.3.30",
"time",
"tokio",
"tracing",
"url",
@ -1470,41 +1342,6 @@ dependencies = [
"syn 2.0.39",
]
[[package]]
name = "darling"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.39",
]
[[package]]
name = "darling_macro"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
dependencies = [
"darling_core",
"quote",
"syn 2.0.39",
]
[[package]]
name = "data-encoding"
version = "2.5.0"
@ -1656,7 +1493,6 @@ dependencies = [
"digest",
"elliptic-curve",
"rfc6979",
"sha2",
"signature",
"spki",
]
@ -1984,7 +1820,7 @@ dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasi",
"wasm-bindgen",
]
@ -2126,12 +1962,6 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "http-range"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
[[package]]
name = "httparse"
version = "1.8.0"
@ -2224,12 +2054,6 @@ dependencies = [
"cc",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "0.5.0"
@ -2272,12 +2096,6 @@ dependencies = [
"generic-array",
]
[[package]]
name = "iocuddle"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8972d5be69940353d5347a1344cb375d9b457d6809b428b05bb1ca2fb9ce007"
[[package]]
name = "ipnet"
version = "2.9.0"
@ -2343,7 +2161,7 @@ dependencies = [
"serde",
"serde_json",
"thiserror",
"time 0.3.30",
"time",
]
[[package]]
@ -2540,7 +2358,7 @@ checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasi",
"windows-sys",
]
@ -2768,15 +2586,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
version = "111.28.0+1.1.1w"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ce95ee1f6f999dfb95b8afd43ebe442758ea2104d1ccb99a94c30db22ae701f"
dependencies = [
"cc",
]
[[package]]
name = "openssl-sys"
version = "0.9.91"
@ -2785,7 +2594,6 @@ checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac"
dependencies = [
"cc",
"libc",
"openssl-src",
"pkg-config",
"vcpkg",
]
@ -2873,12 +2681,6 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "parse-size"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "944553dd59c802559559161f9816429058b869003836120e262e8caec061b7ae"
[[package]]
name = "password-hash"
version = "0.5.0"
@ -3239,27 +3041,6 @@ dependencies = [
"rand",
]
[[package]]
name = "ratls"
version = "0.1.0"
source = "git+https://github.com/Cosmian/tee-tools?branch=get_quote#9085550a81f35c1a869e5d70bf43864225c5bae4"
dependencies = [
"asn1-rs",
"bincode",
"const-oid",
"der",
"ecdsa",
"p256",
"rand_chacha",
"rustls",
"sha2",
"spki",
"tee_attestation",
"thiserror",
"x509-cert",
"x509-parser",
]
[[package]]
name = "rawsql"
version = "0.1.1"
@ -3564,26 +3345,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "scroll"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
dependencies = [
"scroll_derive",
]
[[package]]
name = "scroll_derive"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "scrypt"
version = "0.11.0"
@ -3657,24 +3418,6 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-big-array"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f"
dependencies = [
"serde",
]
[[package]]
name = "serde_bytes"
version = "0.11.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff"
dependencies = [
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.193"
@ -3708,15 +3451,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_plain"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50"
dependencies = [
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@ -3729,82 +3463,6 @@ dependencies = [
"serde",
]
[[package]]
name = "sev"
version = "1.2.1"
source = "git+https://github.com/virtee/sev?rev=a6d6568c96a7a5d6c55d10228a54cda5f3c32e44#a6d6568c96a7a5d6c55d10228a54cda5f3c32e44"
dependencies = [
"bincode",
"bitfield",
"bitflags 1.3.2",
"codicon",
"dirs",
"hex",
"iocuddle",
"lazy_static",
"libc",
"openssl",
"serde",
"serde-big-array",
"serde_bytes",
"static_assertions",
"uuid",
]
[[package]]
name = "sev_quote"
version = "0.1.0"
source = "git+https://github.com/Cosmian/tee-tools?branch=get_quote#9085550a81f35c1a869e5d70bf43864225c5bae4"
dependencies = [
"asn1",
"asn1-rs",
"bincode",
"hex",
"hkdf",
"log",
"reqwest",
"serde",
"sev",
"sha2",
"thiserror",
"uuid",
"x509-parser",
]
[[package]]
name = "sgx_pck_extension"
version = "0.1.1"
source = "git+https://github.com/Cosmian/tee-tools?branch=get_quote#9085550a81f35c1a869e5d70bf43864225c5bae4"
dependencies = [
"asn1",
"asn1-rs",
"thiserror",
"x509-parser",
]
[[package]]
name = "sgx_quote"
version = "0.2.0"
source = "git+https://github.com/Cosmian/tee-tools?branch=get_quote#9085550a81f35c1a869e5d70bf43864225c5bae4"
dependencies = [
"chrono",
"elliptic-curve",
"hex",
"hkdf",
"log",
"p256",
"reqwest",
"rsa",
"scroll",
"serde",
"serde_json",
"sgx_pck_extension",
"sha2",
"thiserror",
"urlencoding",
"x509-parser",
]
[[package]]
name = "sha1"
version = "0.10.6"
@ -4158,12 +3816,6 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
version = "0.25.0"
@ -4253,17 +3905,6 @@ version = "0.12.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a"
[[package]]
name = "tee_attestation"
version = "0.1.0"
source = "git+https://github.com/Cosmian/tee-tools?branch=get_quote#9085550a81f35c1a869e5d70bf43864225c5bae4"
dependencies = [
"bincode",
"sev_quote",
"sgx_quote",
"thiserror",
]
[[package]]
name = "tempfile"
version = "3.8.1"
@ -4322,17 +3963,6 @@ dependencies = [
"once_cell",
]
[[package]]
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
[[package]]
name = "time"
version = "0.3.30"
@ -4642,22 +4272,6 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7830e33f6e25723d41a63f77e434159dad02919f18f55a512b5f16f3b1d77138"
dependencies = [
"base64 0.21.5",
"flate2",
"log",
"once_cell",
"rustls",
"rustls-webpki",
"url",
"webpki-roots 0.25.3",
]
[[package]]
name = "url"
version = "2.5.0"
@ -4683,7 +4297,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
dependencies = [
"getrandom",
"serde",
]
[[package]]
@ -4722,12 +4335,6 @@ dependencies = [
"try-lock",
]
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@ -4999,23 +4606,23 @@ dependencies = [
"ring 0.16.20",
"rusticata-macros",
"thiserror",
"time 0.3.30",
"time",
]
[[package]]
name = "zerocopy"
version = "0.7.26"
version = "0.7.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0"
checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.26"
version = "0.7.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f"
checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a"
dependencies = [
"proc-macro2",
"quote",

View file

@ -53,14 +53,6 @@ dependencies = [
"ci-rust-tests",
]
[tasks.ci-enclave-tests]
category = "dev"
description = "Run Rust tests"
script = '''
cargo build --bin ckms
cargo test --features staging --no-default-features -p kms
'''
#
# Local tests
#

View file

@ -31,8 +31,6 @@ And also some libraries:
**Please refer to the README of the inner directories to have more
information.**
The `sgx` directory contains all the requirements to run the KMS inside an Intel SGX enclave.
You can build a docker containing the KMS server as follow:
```sh
@ -58,3 +56,17 @@ cargo test --no-default-features
## Releases
All releases can be found in the public URL [package.cosmian.com](https://package.cosmian.com/kms/).
## Setup as a `Supervisor` service
Copy the binary `target/release/cosmian_kms` to the remote machine folder according to [cosmian_kms.ini](./resources/supervisor/cosmian_kms.ini) statement (ie: `/usr/sbin/cosmian_kms`).
Copy the [cosmian_kms.ini](./resources/supervisor/cosmian_kms.ini) config file as `/etc/supervisord.d/cosmian_kms.ini` in the remote machine.
Run:
```console
supervisorctl reload
supervisorctl start cosmian_kms
supervisorctl status cosmian_kms
```

View file

@ -1,125 +0,0 @@
FROM ubuntu:22.04 as minimal-sgx
USER root
ENV DEBIAN_FRONTEND=noninteractive
ENV TS=Etc/UTC
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
WORKDIR /root
RUN echo 'APT::Install-Suggests "0";' >> /etc/apt/apt.conf.d/00-docker
RUN echo 'APT::Install-Recommends "0";' >> /etc/apt/apt.conf.d/00-docker
RUN apt-get update && apt-get install --no-install-recommends -qq -y \
build-essential \
protobuf-compiler \
libprotobuf-dev \
libprotobuf-c-dev \
python3 \
gnupg \
ca-certificates \
curl \
libsodium-dev \
tzdata \
&& apt-get -y -q upgrade \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Gramine APT repository
RUN curl -fsSLo /usr/share/keyrings/gramine-keyring.gpg https://packages.gramineproject.io/gramine-keyring.gpg && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/gramine-keyring.gpg] https://packages.gramineproject.io/ jammy main" \
| tee /etc/apt/sources.list.d/gramine.list
# Intel SGX APT repository
RUN curl -fsSLo /usr/share/keyrings/intel-sgx-deb.asc https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel-sgx-deb.asc] https://download.01.org/intel-sgx/sgx_repo/ubuntu jammy main" \
| tee /etc/apt/sources.list.d/intel-sgx.list
# Install Intel SGX dependencies and Gramine
RUN apt-get update && apt-get install -y \
libsgx-launch \
libsgx-urts \
libsgx-quote-ex \
libsgx-epid \
libsgx-dcap-ql \
libsgx-dcap-quote-verify \
linux-base-sgx \
libsgx-dcap-default-qpl \
sgx-aesm-service \
libsgx-aesm-quote-ex-plugin \
gramine && \
rm -rf /var/lib/apt/lists/*
# SGX SDK is installed in /opt/intel directory.
WORKDIR /opt/intel
ARG SGX_SDK_VERSION=2.21
ARG SGX_SDK_INSTALLER=sgx_linux_x64_sdk_2.21.100.1.bin
# Install SGX SDK
RUN curl -fsSLo $SGX_SDK_INSTALLER https://download.01.org/intel-sgx/sgx-linux/$SGX_SDK_VERSION/distro/ubuntu22.04-server/$SGX_SDK_INSTALLER \
&& chmod +x $SGX_SDK_INSTALLER \
&& echo "yes" | ./$SGX_SDK_INSTALLER \
&& rm $SGX_SDK_INSTALLER
#
# Minimal Rust image
#
FROM ubuntu:22.04 as minimal-rust
USER root
ENV DEBIAN_FRONTEND=noninteractive
ENV TS=Etc/UTC
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
WORKDIR /root
RUN apt-get update \
&& apt-get install --no-install-recommends -qq -y \
curl \
build-essential \
libssl-dev \
ca-certificates \
libclang-dev \
libsodium-dev \
pkg-config \
&& apt-get -y -q upgrade \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
COPY . /root
ARG FEATURES
RUN /root/.cargo/bin/cargo build --release --no-default-features ${FEATURES}
#
# Minimal kms
#
FROM minimal-sgx as kms
USER root
ENV DEBIAN_FRONTEND=noninteractive
ENV TS=Etc/UTC
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
ENV KMS_USE_BOOTSTRAP_SERVER =$KMS_USE_BOOTSTRAP_SERVER
ENV KMS_USE_CERTBOT=$KMS_USE_CERTBOT
ENV KMS_CERTBOT_HOSTNAME=$KMS_CERTBOT_HOSTNAME
ENV KMS_CERTBOT_EMAIL=$KMS_CERTBOT_EMAIL
ENV KMS_CERTBOT_USE_TEE_KEY=$KMS_CERTBOT_USE_TEE_KEY
WORKDIR /root
RUN mkdir -p scripts
# private_data, public_data and shared_data are supposed to be given as parameters of the docker run
COPY --from=minimal-rust /root/target/release/cosmian_kms_server scripts/server
COPY ci/sgx/build_and_run.sh ci/sgx/Makefile ci/sgx/kms.manifest.template /root/
ENTRYPOINT ["./build_and_run.sh"]

View file

@ -1,47 +0,0 @@
ARCH_LIBDIR ?= /lib/$(shell $(CC) -dumpmachine)
SGX_SIGNER_KEY ?= /opt/cosmian-internal/cosmian-signer-key.pem
SGX_SIGNER_PUBLIC_KEY = cosmian-signer-key.pub
ifeq ($(DEBUG),1)
GRAMINE_LOG_LEVEL = debug
# can also use "all" for having something like `strace`
else
GRAMINE_LOG_LEVEL = error
endif
.PHONY: all
all: kms.manifest
ifeq ($(SGX),1)
all: kms.manifest.sgx kms.sig
endif
kms.manifest: kms.manifest.template
gramine-manifest \
-Dlog_level=$(GRAMINE_LOG_LEVEL) \
-Darch_libdir=$(ARCH_LIBDIR) \
-Dentrypoint_abs="$(PWD)/scripts" \
-Dentrypoint="/scripts" \
-Dsgx_signer_public_key=$(SGX_SIGNER_PUBLIC_KEY) \
-Dkms_use_bootstrap_server=$(KMS_USE_BOOTSTRAP_SERVER) \
-Dkms_use_cerbot=$(KMS_USE_CERTBOT) \
-Dkms_cerbot_hostname=$(KMS_CERTBOT_HOSTNAME) \
-Dkms_cerbot_email=$(KMS_CERTBOT_EMAIL) \
-Dkms_cerbot_use_tee_key=$(KMS_CERTBOT_USE_TEE_KEY) \
-Dkms_domain="$(KMS_DOMAIN)" \
$< >$@
kms.manifest.sgx: kms.manifest
gramine-sgx-sign \
--key $(SGX_SIGNER_KEY) \
--manifest kms.manifest \
--output $@
openssl rsa -in $(SGX_SIGNER_KEY) -pubout -out public_data/$(SGX_SIGNER_PUBLIC_KEY)
kms.sig: kms.manifest.sgx
.PHONY: clean
clean:
$(RM) *.manifest *.manifest.sgx *.sig OUTPUT scripts/testdir/*
.PHONY: distclean
distclean: clean

View file

@ -1,27 +0,0 @@
#!/bin/bash
SGX_SIGNER_KEY="/opt/cosmian-internal/cosmian-signer-key.pem"
mkdir -p /root/public_data/
mkdir -p /root/private_data/
if [ $# -eq 0 ]; then
if ! [ -e "/dev/sgx_enclave" ]; then
echo "You are not running on an sgx machine"
echo "If you want to compute the MR_ENCLAVE, re-run with --emulation parameter"
exit 1
fi
make SGX=1 SGX_SIGNER_KEY=$SGX_SIGNER_KEY KMS_USE_BOOTSTRAP_SERVER="${KMS_USE_BOOTSTRAP_SERVER}" KMS_USE_CERTBOT="${KMS_USE_CERTBOT}" KMS_CERTBOT_HOSTNAME="${KMS_CERTBOT_HOSTNAME}" KMS_CERTBOT_EMAIL="${KMS_CERTBOT_EMAIL}" KMS_CERTBOT_USE_TEE_KEY="${KMS_CERTBOT_USE_TEE_KEY}" && gramine-sgx ./kms
elif [ $# -eq 1 ] && [ "$1" = "--emulation" ]; then
mkdir /opt/cosmian-internal
# Generate a dummy key. `MR_ENCLAVE` does not depend on it.
openssl genrsa -3 -out $SGX_SIGNER_KEY 3072
# Compile but don't start
make SGX=1 SGX_SIGNER_KEY=$SGX_SIGNER_KEY KMS_USE_BOOTSTRAP_SERVER="${KMS_USE_BOOTSTRAP_SERVER}" KMS_USE_CERTBOT="${KMS_USE_CERTBOT}" KMS_CERTBOT_HOSTNAME="${KMS_CERTBOT_HOSTNAME}" KMS_CERTBOT_EMAIL="${KMS_CERTBOT_EMAIL}" KMS_CERTBOT_USE_TEE_KEY="${KMS_CERTBOT_USE_TEE_KEY}"
# Note: if `public_data` is mounted inside the docker, the user can read `kms.manifest.kms` from outside the docker
else
echo "Usage: $0 [--emulation]"
echo ""
echo "Using --emulation enables you to get the MR_ENCLAVE of the enclave server"
echo "You don't need to use an SGX machine to use --emulation param"
fi

View file

@ -1,6 +0,0 @@
{
"kms_server_url": "https://kms.sgx.ci.cosmian.dev:9998",
"kms_access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjVVU1FrSVlULW9QMWZrcjQtNnRrciJ9.eyJuaWNrbmFtZSI6InRlY2giLCJuYW1lIjoidGVjaEBjb3NtaWFuLmNvbSIsInBpY3R1cmUiOiJodHRwczovL3MuZ3JhdmF0YXIuY29tL2F2YXRhci81MmZiMzFjOGNjYWQzNDU4MTIzZDRmYWQxNDA4NTRjZj9zPTQ4MCZyPXBnJmQ9aHR0cHMlM0ElMkYlMkZjZG4uYXV0aDAuY29tJTJGYXZhdGFycyUyRnRlLnBuZyIsInVwZGF0ZWRfYXQiOiIyMDIzLTA1LTMwVDA5OjMxOjExLjM4NloiLCJlbWFpbCI6InRlY2hAY29zbWlhbi5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImlzcyI6Imh0dHBzOi8va21zLWNvc21pYW4uZXUuYXV0aDAuY29tLyIsImF1ZCI6IkszaXhldXhuVDVrM0Roa0tocWhiMXpYbjlFNjJGRXdJIiwiaWF0IjoxNjg1NDM5MDc0LCJleHAiOjE2ODU0NzUwNzQsInN1YiI6ImF1dGgwfDYzZDNkM2VhOTNmZjE2ANDJjNzdkZjkyOCIsInNpZCI6ImJnVUNuTTNBRjVxMlpaVHFxMTZwclBCMi11Z0NNaUNPIiwibm9uY2UiOiJVRUZWTlZWeVluWTVUbHBwWjJScGNqSmtVMEZ4TmxkUFEwc3dTVGMwWHpaV2RVVmtkVnBEVGxSMldnPT0ifQ.HmU9fFwZ-JjJVlSy_PTei3ys0upeWQbWWiESmKBtRSClGnAXJNCpwuP4Jw7fgKn-8IBf-PYmP1_54u2Rw3RcJFVl7EblVoGMghYxVq5hViGpd00st3VwZmyCwOUz2CE5RBnBAoES4C8xA3zWg6oau0xjFQbC3jNU20eyFYMDewXA8UXCHQrEiQ56ylqSbyqlBbQIWbmOO4m5w2WDkx0bVyyJ893JfIJr_NANEQMJITYo8Mp_iHCyKp7llsfgCt07xN8ZqnsrMsJ15zC1n50bHGrTQisxURS1dpuFXF1hfrxhzogxYMX8CEISjsFgROjPY84GRMmvpYZfyaJbDDql3A",
"kms_database_secret": "eyJncm91cF9pZCI6MTY5NjE5MjAzMzQ1MDY0MDQxNjY1ODIyNzgwNjczNDY1ODkyNjcyLCJrZXkiOiJhN2EyNWY2YWUxMzExODMyYTBiYmRkZDNjMjk3ZjhjYTAxZTg4OWEzNzFlNjNhZmMyNjU4MDc2NzE1MmQ4YTA2In0=",
"accept_invalid_certs": true
}

View file

@ -1,76 +0,0 @@
loader.entrypoint = "file:{{ gramine.libos }}"
libos.entrypoint = "{{ entrypoint }}/server"
# We don't use argv, therefore we have to set argv[0]
loader.argv0_override = "kms_server"
# loader.argv_src_file = "file:scripts/args"
loader.log_level = "{{ log_level }}"
# Currently required for Tokio (eventfd is done by the host not the enclave, so less secure)
sys.insecure__allow_eventfd = true
# This specifies the stack size of each thread in each Gramine process
# Note: if you remove that, the KMS won't work. Errors you can get:
# - "thread panicked while processing panic. aborting."
# - "The futex facility returned an unexpected error code."
sys.stack.size = "1G"
sys.enable_extra_runtime_domain_names_conf = true
loader.env.LD_LIBRARY_PATH = "/lib:/lib64:{{ arch_libdir }}:/usr/{{ arch_libdir }}"
loader.env.KMS_SGX_PUBLIC_SIGNER_KEY_FILENAME = "{{ sgx_signer_public_key }}"
loader.env.KMS_TEE_DIR_PATH = "/public_data"
loader.env.KMS_ROOT_DATA_PATH = "/private_data"
loader.env.RUST_BACKTRACE="0"
loader.env.KMS_USE_BOOTSTRAP_SERVER = "{{ kms_use_bootstrap_server }}"
loader.env.KMS_USE_CERTBOT = "{{ kms_use_cerbot }}"
loader.env.KMS_CERTBOT_HOSTNAME = "{{ kms_cerbot_hostname }}"
loader.env.KMS_CERTBOT_EMAIL = "{{ kms_cerbot_email }}"
loader.env.KMS_CERTBOT_USE_TEE_KEY = "{{ kms_cerbot_use_tee_key }}"
sgx.nonpie_binary = true
sgx.remote_attestation = 'dcap'
sgx.enclave_size = "16G"
sgx.max_threads = 256
sgx.debug = false
sgx.isvprodid = 1
sgx.isvsvn = 10
# File to mount into the enclave
fs.mounts = [
{ type = "chroot", uri = "file:{{ gramine.runtimedir() }}", path = "/lib" },
{ type = "chroot", uri = "file:{{ arch_libdir }}", path = "{{ arch_libdir }}" },
{ type = "chroot", uri = "file:/usr/{{ arch_libdir }}", path = "/usr/{{ arch_libdir }}" },
{ type = "chroot", uri = "file:/etc", path = "/etc" },
{ type = "chroot", uri = "file:{{ entrypoint_abs }}", path = "{{ entrypoint }}" },
{ type = "tmpfs", path = "/tmp" },
{ type = "encrypted", uri = "file:private_data", path = "/private_data", key_name = "_sgx_mrenclave" },
]
# Public path
sgx.allowed_files = [
"file:public_data"
]
# Files to hash at build time and allowed to be accessed in runtime if hashes match
sgx.trusted_files = [
"file:{{ entrypoint_abs }}/server",
"file:{{ gramine.libos }}",
"file:{{ gramine.runtimedir() }}/",
"file:{{ arch_libdir }}/",
"file:/etc/nsswitch.conf",
"file:/etc/group",
"file:/etc/passwd",
"file:/etc/host.conf",
"file:/etc/gai.conf",
"file:/etc/resolv.conf",
"file:/etc/localtime",
"file:/etc/ld.so.cache",
"file:{{ arch_libdir }}/libsodium.so.23",
"file:/usr/lib/ssl/certs/",
"file:/etc/ssl/certs/ca-certificates.crt",
]

View file

@ -12,6 +12,7 @@ path = "src/main.rs"
[features]
# Staging is used to run tests with the remote kms test server. Otherwise, the test runs a local kms server.
staging = []
fips = ["cosmian_kms_utils/fips"]
[dependencies]
actix-web = { workspace = true }
@ -23,20 +24,14 @@ cosmian_kms_client = { path = "../client" }
cosmian_kms_utils = { path = "../utils" }
cosmian_logger = { path = "../logger" }
der = { workspace = true }
hex = { workspace = true }
oauth2 = "4.4"
## openssl is only used to parse PKCS#12 files because native support is not yet available in Rust yet.
## It should not be used for anything else and will be removed once native support is available.
openssl = { version = "0.10", features = ["vendored"] }
pem = "3.0"
rand = "0.8"
ratls = { workspace = true }
reqwest = { workspace = true }
rustls = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
strum = { workspace = true }
tee_attestation = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
@ -54,6 +49,7 @@ cosmian_kms_client = { path = "../client", default-features = false }
cosmian_kms_server = { path = "../server", features = [
"insecure",
], default-features = false }
openssl = { workspace = true }
predicates = "3.0"
regex = { version = "1.10", default-features = false }
tempfile = "3.8"

View file

@ -91,62 +91,6 @@ You can perform the following operations concerning to the users-to-objects perm
- `remove` Remove an access authorization for an object to a user
- `shared` List objects shared for the current user
### Quote and remote attestation
If the KMS server is running inside an enclave, you can and should verify its trustworthiness.
To do so, use `trust` subcommand. Doing that, the `cli` will:
- Ask the kms server to generate a quote containing the public certificate of the kms server and a nonce (a randomly generated string to make the quote unique each time)
- Send the quote to Azure Microsoft to proceed a remote attestation
- Proceed some trust checks
- Export various files on your filesystem to let you manually verify them.
From [gramine docs](https://gramine.readthedocs.io/en/latest/sgx-intro.html#term-sgx-quote), you can read: "*The SGX quote is the proof of trustworthiness of the enclave and is used during Remote Attestation. The attesting enclave generates the enclave-specific SGX Report, sends the request to the Quoting Enclave using Local Attestation, and the Quoting Enclave returns back the SGX quote with the SGX report embedded in it. The resulting SGX quote contains the enclave's measurement, attributes and other security-relevant fields, and is tied to the identity of the Quoting Enclave to prove its authenticity. The obtained SGX quote may be later sent to the verifying remote party, which examines the SGX quote and gains trust in the remote enclave.*"
#### Quote report data
The report data contains attributes smartly chosen to make a decision on the trustworthiness of the enclave.
- The __ssl certificate__. This certificate is encrypted using the `mr_enclave` key. Therefore if the server is updated, the certificates will be also updated and the quote will vary. Moreover this parameter is public, so you are plenty aware when the certificate changes.
- The __nonce__ to make the quote unique each time the user want a proof of trust. It uses an arbitrary and non predictable string. The kms server can't therefore send a previous verify version of the quote.
#### Automatic trust checks
The cli automatically checks:
- If the kms server runs inside an sgx enclave known by *Intel*
- If the quote inside the remote attestation is the same than the quote returning by the enclave
- If the `mr_enclave` and `mr_signer` are the same between the remote attestation and the quote
- If the `mr_enclave` and `mr_signer` are the expected ones. See below.
- If the current time is contained into the `iat` and the `exp` time of the remote attestation
- If the quote's report data is both the same in the remote attestation and in the quote
#### `mr_signer`
This value enables you to verify that the KMS is running inside an enclave which belongs to *Cosmian*. Indeed this value is a `sha256` hash of the public key used to sign the enclave.
This value will be compute by the CLI and compared against the values obtained from the quote and the remote attestation.
If the value is altered, it could mean that you are not using the *Cosmian* KMS in the *Cosmian* infrastructures. You shouldn't proceed and you should report that incident to us.
#### `mr_enclave`
This value enables you to verify that the KMS code and libraries inside the enclave are the same as the code you can read on [*Cosmian* Github](https://github.com/Cosmian).
This value is not compute by the CLI. You can get the open-sourced KMS docker, read the `mr_enclave` value from it and give it to the CLI to check it. See [README.md](../../sgx/README.md#emulate) for more details.
If the value is altered, it could mean that you are not using the KMS from *Cosmian* but a modified one. You shouldn't proceed and you should report that incident to us.
#### Exported files
The files you can manually verify are:
- The quote: `quote.raw` and `quote.struct`
- The manifest of the enclave containing the hashes of all trusted files and the running context (env variables, etc.): `manifest.sgx`
- The remote attestation itself: `remote_attestation`
- The enclave and the ssl certificate: `enclave.pub` and `ssl.cert`
## Testing
```sh
@ -156,9 +100,7 @@ cargo test -p cosmian_kms_cli
A KMS server is started by the test. Make sure, you don't start another one by yourself.
You can also test using a remote KMS running inside an enclave. First, generate and start a docker as described in [README.md](../../sgx/README.md).
Then:
You can also test using a remote KMS:
```sh
cargo build --bin cosmian_kms_cli

View file

@ -1,170 +0,0 @@
use std::path::PathBuf;
use clap::{Args, Parser};
use cosmian_kms_client::BootstrapRestClient;
use crate::{cli_bail, error::CliError};
/// Provide configuration and start the KMS server via the bootstrap server.
///
/// When the server is started using the bootstrap server,
/// this command is used to provide configuration information, such as
/// - database configuration
/// - PKCS12 to use as the KMS HTTPS server certificate
/// and start the configured KMS server.
#[derive(Parser)]
#[clap(verbatim_doc_comment)]
pub struct BootstrapServerAction {
#[clap(flatten)]
pub db: DatabaseConfig,
#[clap(flatten)]
pub pkcs12: Pkcs12Config,
}
impl BootstrapServerAction {
pub async fn process(
&self,
bootstrap_rest_client: &BootstrapRestClient,
) -> Result<(), CliError> {
println!("Server response:");
// set the password if provided, if not set the empty string
bootstrap_rest_client
.set_pkcs12_password(&self.pkcs12.https_p12_password)
.await?;
// upload the PKCS12 file if provided
if let Some(pkcs12_file) = &self.pkcs12.https_p12_file {
let response = bootstrap_rest_client.upload_pkcs12(pkcs12_file).await?;
println!(" -> {}", response.success);
}
// set the database configuration
if let Some(database_type) = &self.db.database_type {
let response = match database_type.as_str() {
"redis-findex" => {
let database_url = self.db.database_url.as_ref().ok_or_else(|| {
CliError::Default("Missing the database url for redis-findex".to_string())
})?;
let redis_master_password =
self.db.redis_master_password.as_ref().ok_or_else(|| {
CliError::Default(
"Missing the Redis master password for redis-findex".to_string(),
)
})?;
let redis_findex_label =
self.db.redis_findex_label.as_ref().ok_or_else(|| {
CliError::Default(
"Missing the Findex label for redis-findex".to_string(),
)
})?;
bootstrap_rest_client
.set_redis_findex_config(
database_url,
redis_master_password,
redis_findex_label,
)
.await?
}
"postgresql" => {
if let Some(database_url) = &self.db.database_url {
bootstrap_rest_client
.set_postgresql_config(database_url)
.await?
} else {
cli_bail!("Missing the database url for postgresql")
}
}
"mysql" => {
if let Some(database_url) = &self.db.database_url {
bootstrap_rest_client.set_mysql_config(database_url).await?
} else {
cli_bail!("Missing the database url for mysql")
}
}
"sqlite" => {
bootstrap_rest_client
.set_sqlite_config(&self.db.sqlite_path)
.await?
}
"sqlite-enc" => {
bootstrap_rest_client
.set_sqlite_enc_config(&self.db.sqlite_path)
.await?
}
_ => {
cli_bail!("Invalid database type");
}
};
println!(" -> {}", response.success);
}
let response = bootstrap_rest_client
.start_kms_server(self.db.clear_database)
.await?;
println!(" -> {}", response.success);
Ok(())
}
}
/// Configuration for the database
#[derive(Args, Clone)]
pub struct DatabaseConfig {
/// The database type of the KMS server:
/// - postgresql: PostgreSQL. The database url must be provided
/// - mysql: MySql or MariaDB. The database url must be provided
/// - sqlite: SQLite. The data will be stored at the sqlite_path directory
/// - sqlite-enc: SQLite encrypted at rest. The data will be stored at the sqlite_path directory.
/// A key must be supplied on every call
/// - redis-findex: and redis database with encrypted data and encrypted indexes thanks to Findex.
/// The Redis database url must be provided, as well as the redis-master-password and the redis-findex-label
/// _
#[clap(
long,
value_parser(["postgresql", "mysql", "sqlite", "sqlite-enc", "redis-findex"]),
verbatim_doc_comment
)]
pub database_type: Option<String>,
/// The url of the database for postgresql, mysql or findex-redis
#[clap(
long,
required_if_eq_any([("database_type", "postgresql"), ("database_type", "mysql"), ("database_type", "redis-findex")])
)]
pub database_url: Option<String>,
/// The directory path of the sqlite or sqlite-enc
#[clap(
long,
default_value = "./sqlite-data",
required_if_eq_any([("database_type", "sqlite"), ("database_type", "sqlite-enc")])
)]
pub sqlite_path: PathBuf,
/// redis-findex: a master password used to encrypt the Redis data and indexes
#[clap(long, required_if_eq("database_type", "redis-findex"))]
pub redis_master_password: Option<String>,
/// redis-findex: a public arbitrary label that can be changed to rotate the Findex ciphertexts
/// without changing the key
#[clap(long, required_if_eq("database_type", "redis-findex"))]
pub redis_findex_label: Option<String>,
/// Clear the database on start.
/// WARNING: This will delete ALL the data in the database
#[clap(long, default_value = "false", verbatim_doc_comment)]
pub clear_database: bool,
}
#[derive(Args, Clone)]
pub struct Pkcs12Config {
/// The KMS server optional PKCS#12 Certificates and Key file. If provided, this will start the server in HTTPS mode.
#[clap(long)]
pub https_p12_file: Option<PathBuf>,
/// The password to open the PKCS#12 Certificates and Key file if not an empty string
#[clap(long, default_value = "")]
pub https_p12_password: String,
}

View file

@ -1,5 +1,4 @@
pub mod access;
pub mod bootstrap;
pub mod certificates;
pub mod cover_crypt;
pub mod elliptic_curves;
@ -9,5 +8,4 @@ pub mod markdown;
pub mod new_database;
pub mod shared;
pub mod symmetric;
pub mod verify;
pub mod version;

View file

@ -2,14 +2,12 @@ use std::path::PathBuf;
use base64::{engine::general_purpose, Engine as _};
use clap::Parser;
use cloudproof::reexport::crypto_core::CsRng;
use cosmian_kmip::kmip::kmip_types::CryptographicAlgorithm;
use cosmian_kms_client::KmsRestClient;
use cosmian_kms_utils::crypto::{
password_derivation::derive_key_from_password, symmetric::create_symmetric_key,
wrap::wrap_key_block,
};
use rand::SeedableRng;
use crate::{
actions::shared::{
@ -93,8 +91,7 @@ impl WrapKeyAction {
cli_bail!("one of the wrapping options must be specified");
};
let mut rng = CsRng::from_entropy();
wrap_key_block(&mut rng, object.key_block_mut()?, &wrapping_key, None)?;
wrap_key_block(object.key_block_mut()?, &wrapping_key, None)?;
// set the output file path to the input file path if not specified
let output_file = self

View file

@ -1,110 +0,0 @@
use std::{
fs,
path::{Path, PathBuf},
};
use clap::Parser;
use cosmian_kms_utils::tee::forge_report_data;
use openssl::x509::X509;
use rand::Rng;
use ratls::verify::get_server_certificate;
use tee_attestation::{verify_quote, TeeMeasurement};
use tokio::task::spawn_blocking;
use crate::{
config::{CliConf, TeeConf},
error::{result::CliResultHelper, CliError},
};
/// Query the KMS to check its trustworthiness. Validate the TLS certificate to use in any further queries.
#[derive(Parser, Debug)]
pub struct TeeAction {
/// The path to store working files (quote, certificate, ...)
#[clap(default_value = "/tmp/kms")]
export_path: PathBuf,
}
impl TeeAction {
pub async fn process(&self, conf: &CliConf) -> Result<(), CliError> {
// Create the export directory if it does not exist
if !Path::new(&self.export_path).exists() {
fs::create_dir_all(&self.export_path)?;
}
let server_url = conf.kms_server_url()?;
// Get the KMS certificate
let certificate = get_server_certificate(
server_url
.host_str()
.ok_or_else(|| CliError::Default("Host not found in server url".to_string()))?,
server_url.port().unwrap_or(443).into(),
)
.map_err(|e| CliError::Default(format!("Can't get KMS server certificate: {e}")))?;
let certificate = X509::from_der(&certificate)
.map_err(|e| CliError::Default(format!("Can't convert certificate from DER: {e}")))?
.to_pem()
.map_err(|e| CliError::Default(format!("Can't convert certificate to PEM: {e}")))?;
let cert_path = self.export_path.join("cert.pem");
fs::write(&cert_path, &certificate)?;
println!("The KMS PEM certificate has been saved at {cert_path:?}");
// Let's use this certificate when querying the KMS to get the quote
let mut local_conf = conf.clone();
let verified_cert = Some(String::from_utf8_lossy(&certificate).to_string());
if let Some(mut local_tee_conf) = local_conf.tee_conf {
local_tee_conf.verified_cert = verified_cert;
local_conf.tee_conf = Some(local_tee_conf);
} else {
local_conf.tee_conf = Some(TeeConf {
verified_cert,
..Default::default()
});
}
let kms_rest_client = local_conf.initialize_kms_client()?;
// Generate a nonce to make the quote unique. Use an arbitrary and non predictable string.
let nonce = rand::thread_rng().gen::<[u8; 32]>();
let nonce_path = self.export_path.join("nonce.bin");
fs::write(&nonce_path, nonce)?;
println!("The random nonce has been saved at {nonce_path:?}");
// Get the quote from the kms
let quote = kms_rest_client
.get_attestation_report(&nonce)
.await
.with_context(|| "Can't execute the query on the kms server")?;
// Save the quote
let quote_raw_path = self.export_path.join("quote.bin");
fs::write(&quote_raw_path, &quote)?;
println!("The raw quote has been saved at {quote_raw_path:?}");
// Let's verify the quote
let report_data = forge_report_data(&nonce, &certificate)?;
let tee_conf = if let Some(tee_conf) = conf.tee_conf.clone() {
tee_conf.try_into()?
} else {
TeeMeasurement::default()
};
match spawn_blocking(move || verify_quote(&quote, &report_data, tee_conf))
.await
.map_err(|e| CliError::Default(format!("Can't verify quote: {e}")))?
{
Ok(()) => println!("Verification succeed"),
Err(e) => return Err(e.into()),
}
// Now, the user doesn't need to verify the quote each time it queries the KMS since it forces the certificate to be that one.
local_conf.save()?;
println!("You configuration file has been updated to secure the further calls to the KMS");
Ok(())
}
}

View file

@ -5,12 +5,10 @@ use std::{
path::PathBuf,
};
use cosmian_kms_client::{BootstrapRestClient, KmsRestClient};
use hex::decode;
use openssl::x509::X509;
use cosmian_kms_client::KmsRestClient;
use der::{DecodePem, Encode};
use rustls::Certificate;
use serde::{Deserialize, Serialize};
use tee_attestation::{SevMeasurement, SgxMeasurement, TeeMeasurement};
use url::Url;
use crate::error::{result::CliResultHelper, CliError};
@ -58,83 +56,6 @@ fn not(b: &bool) -> bool {
!*b
}
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone, Default)]
pub struct TeeConf {
// verified_cert = Some(PEM) means that the leaf certificate needs to be the exact one
// used when verifying the quote. This option should be set if the KMS has been deployed on a TEE. None is unsecured.
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) verified_cert: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) mr_enclave: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) public_signer_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) sev_measurement: Option<String>,
}
impl TryInto<TeeMeasurement> for TeeConf {
type Error = CliError;
fn try_into(self) -> Result<TeeMeasurement, Self::Error> {
let mrenclave = if let Some(v) = &self.mr_enclave {
Some(
decode(v)
.map_err(|_| {
CliError::Default("Invalid hexadecimal format for mr enclave".to_string())
})?
.as_slice()
.try_into()
.map_err(|e| {
CliError::Default(format!("Bad MR enclave bytes-size: error: {e}"))
})?,
)
} else {
None
};
let sev_measurement = if let Some(v) = &self.sev_measurement {
Some(
decode(v)
.map_err(|_| {
CliError::Default("Invalid hexadecimal format for mr enclave".to_string())
})?
.as_slice()
.try_into()
.map_err(|e| {
CliError::Default(format!("Bad SEV measurement bytes-size: error: {e}"))
})?,
)
} else {
None
};
let measurement = match (self.public_signer_key, mrenclave, sev_measurement) {
(None, None, None) => TeeMeasurement {
sgx: None,
sev: None,
},
(Some(s), Some(e), None) => TeeMeasurement {
sgx: Some(SgxMeasurement {
public_signer_key_pem: s.to_string(),
mr_enclave: e,
}),
sev: None,
},
(None, None, Some(m)) => TeeMeasurement {
sgx: None,
sev: Some(SevMeasurement(m)),
},
_ => {
return Err(CliError::Default(
"Bad measurements combination".to_string(),
))
}
};
Ok(measurement)
}
}
/// The configuration that is used by the Login command
/// to perform the `OAuth2` authorize code flow and obtain an access token.
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)]
@ -165,9 +86,7 @@ pub struct CliConf {
pub(crate) accept_invalid_certs: bool,
pub(crate) kms_server_url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) bootstrap_server_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tee_conf: Option<TeeConf>,
pub(crate) verified_cert: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) kms_access_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
@ -186,14 +105,6 @@ impl CliConf {
pub fn kms_server_url(&self) -> Result<Url, CliError> {
Ok(Url::parse(&self.kms_server_url)?)
}
pub fn bootstrap_server_url(&self) -> Result<Option<Url>, CliError> {
if let Some(url) = &self.bootstrap_server_url {
Ok(Some(Url::parse(url)?))
} else {
Ok(None)
}
}
}
impl Default for CliConf {
@ -201,8 +112,7 @@ impl Default for CliConf {
Self {
accept_invalid_certs: false,
kms_server_url: "http://0.0.0.0:9998".to_string(),
bootstrap_server_url: None,
tee_conf: None,
verified_cert: None,
kms_access_token: None,
kms_database_secret: None,
ssl_client_pkcs12_path: None,
@ -221,7 +131,6 @@ impl Default for CliConf {
/// {
/// "accept_invalid_certs": false,
/// "kms_server_url": "http://127.0.0.1:9998",
/// "bootstrap_server_url": "https://127.0.0.1:9998",
/// "kms_access_token": "AA...AAA",
/// "kms_database_secret": "BB...BBB",
/// "ssl_client_pkcs12_path": "/path/to/client.p12",
@ -301,17 +210,12 @@ impl CliConf {
self.ssl_client_pkcs12_password.as_deref(),
self.kms_database_secret.as_deref(),
self.accept_invalid_certs,
match &self.tee_conf {
Some(tee_conf) => {
if let Some(certificate) = &tee_conf.verified_cert {
Some(Certificate(
X509::from_pem(certificate.as_bytes())?.to_der()?,
))
} else {
None
}
}
None => None,
if let Some(certificate) = &self.verified_cert {
Some(Certificate(
x509_cert::Certificate::from_pem(certificate.as_bytes())?.to_der()?,
))
} else {
None
},
self.jwe_public_key.as_deref(),
)
@ -324,31 +228,6 @@ impl CliConf {
Ok(kms_rest_client)
}
pub fn initialize_bootstrap_client(&self) -> Result<BootstrapRestClient, CliError> {
// Instantiate a Bootstrap server REST client with the given configuration
let bootstrap_rest_client = BootstrapRestClient::instantiate(
self.bootstrap_server_url
.as_ref()
.unwrap_or(&self.kms_server_url.replace("http://", "https://")),
self.kms_access_token.as_deref(),
self.ssl_client_pkcs12_path.as_deref(),
self.ssl_client_pkcs12_password.as_deref(),
if let Some(tee_conf) = self.tee_conf.clone() {
tee_conf.try_into()?
} else {
TeeMeasurement::default()
},
)
.with_context(|| {
format!(
"Unable to instantiate a Bootstrap server REST client {}",
&self.kms_server_url
)
})?;
Ok(bootstrap_rest_client)
}
}
#[cfg(test)]

View file

@ -8,7 +8,6 @@ use cosmian_kmip::{
};
use cosmian_kms_client::RestClientError;
use cosmian_kms_utils::error::KmipUtilsError;
use openssl::error::ErrorStack;
use pem::PemError;
use thiserror::Error;
@ -45,10 +44,6 @@ pub enum CliError {
#[error("Server error: {0}")]
ServerError(String),
// Any errors related to a bad behavior of the server concerning the SGX environment
#[error("Unexpected sgx error: {0}")]
SGXError(String),
// Any actions of the user which is not allowed
#[error("Access denied: {0}")]
Unauthorized(String),
@ -73,10 +68,6 @@ pub enum CliError {
#[error("{0}")]
Default(String),
// TEE errors
#[error(transparent)]
TeeAttestationError(#[from] tee_attestation::error::Error),
// Url parsing errors
#[error(transparent)]
UrlParsing(#[from] url::ParseError),
@ -118,12 +109,6 @@ impl From<cloudproof::reexport::crypto_core::reexport::pkcs8::Error> for CliErro
}
}
impl From<ErrorStack> for CliError {
fn from(e: ErrorStack) -> Self {
Self::Conversion(e.to_string())
}
}
impl From<TryFromSliceError> for CliError {
fn from(e: TryFromSliceError) -> Self {
Self::Conversion(e.to_string())

View file

@ -4,7 +4,6 @@ use clap::{CommandFactory, Parser, Subcommand};
use cosmian_kms_cli::{
actions::{
access::AccessAction,
bootstrap::BootstrapServerAction,
certificates::CertificatesCommands,
cover_crypt::CovercryptCommands,
elliptic_curves::EllipticCurveCommands,
@ -14,14 +13,12 @@ use cosmian_kms_cli::{
new_database::NewDatabaseAction,
shared::{GetAttributesAction, LocateObjectsAction},
symmetric::SymmetricCommands,
verify::TeeAction,
version::ServerVersionAction,
},
config::CliConf,
error::CliError,
};
use cosmian_logger::log_utils::log_init;
use tokio::task::spawn_blocking;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
@ -34,7 +31,6 @@ struct Cli {
enum CliCommands {
#[command(subcommand)]
AccessRights(AccessAction),
BootstrapStart(BootstrapServerAction),
#[command(subcommand)]
Cc(CovercryptCommands),
#[command(subcommand)]
@ -47,7 +43,6 @@ enum CliCommands {
ServerVersion(ServerVersionAction),
#[command(subcommand)]
Sym(SymmetricCommands),
Verify(TeeAction),
Login(LoginAction),
Logout(LogoutAction),
#[clap(hide = true)]
@ -68,19 +63,6 @@ async fn main_() -> Result<(), CliError> {
let opts = Cli::parse();
let conf = CliConf::load()?;
if let CliCommands::Verify(action) = opts.command {
action.process(&conf).await?;
return Ok(())
}
if let CliCommands::BootstrapStart(action) = opts.command {
let bootstrap_rest_client = spawn_blocking(move || conf.initialize_bootstrap_client())
.await
.map_err(|e| CliError::Default(e.to_string()))??;
action.process(&bootstrap_rest_client).await?;
return Ok(())
}
if let CliCommands::Markdown(action) = opts.command {
let command = <Cli as CommandFactory>::command();
action.process(&command).await?;
@ -98,7 +80,6 @@ async fn main_() -> Result<(), CliError> {
CliCommands::Certificates(action) => action.process(&kms_rest_client).await?,
CliCommands::NewDatabase(action) => action.process(&kms_rest_client).await?,
CliCommands::ServerVersion(action) => action.process(&kms_rest_client).await?,
CliCommands::BootstrapStart(_) | CliCommands::Verify(_) => {}
CliCommands::GetAttributes(action) => action.process(&kms_rest_client).await?,
CliCommands::Login(action) => action.process().await?,
CliCommands::Logout(action) => action.process().await?,

View file

@ -23,22 +23,22 @@ pub async fn test_all_authentications() -> Result<(), CliError> {
// let us not make other test cases fail
const PORT: u16 = 9999;
// plaintext no auth
let ctx = start_test_server_with_options(PORT, false, false, false, false, false).await;
let ctx = start_test_server_with_options(PORT, false, false, false, false).await;
run_cli_command(&ctx.owner_cli_conf_path);
ctx.stop_server().await;
// plaintext token auth
let ctx = start_test_server_with_options(PORT, true, false, false, false, false).await;
let ctx = start_test_server_with_options(PORT, true, false, false, false).await;
run_cli_command(&ctx.owner_cli_conf_path);
ctx.stop_server().await;
// tls token auth
let ctx = start_test_server_with_options(PORT, true, true, false, false, false).await;
let ctx = start_test_server_with_options(PORT, true, true, false, false).await;
run_cli_command(&ctx.owner_cli_conf_path);
ctx.stop_server().await;
// tls client cert auth
let ctx = start_test_server_with_options(PORT, false, true, true, false, false).await;
let ctx = start_test_server_with_options(PORT, false, true, true, false).await;
run_cli_command(&ctx.owner_cli_conf_path);
ctx.stop_server().await;

View file

@ -1,60 +0,0 @@
use std::process::Command;
use assert_cmd::prelude::*;
use super::{utils::recover_cmd_logs, PROG_NAME};
use crate::{
config::KMS_CLI_CONF_ENV, error::CliError, tests::utils::start_test_server_with_options,
};
pub(crate) const SUB_COMMAND: &str = "bootstrap-start";
#[tokio::test]
#[ignore] // Need a tee runner to test this feature
pub async fn test_bootstrap_server() -> Result<(), CliError> {
// init the test server
// since we are going to rewrite the conf, use a different port
let ctx = start_test_server_with_options(29997, false, false, false, false, true).await;
let mut args: Vec<&str> = vec![];
// No database parameters are supplied, start should fail
assert!(run_bootstrap_start(&ctx.owner_cli_conf_path, &args).is_err());
// The database type is not supplied, start should fail
args.extend_from_slice(&["--sqlite-path", "./sqlite-data"]);
assert!(run_bootstrap_start(&ctx.owner_cli_conf_path, &args).is_err());
// The database type is supplied, start should succeed (in HTTP mode)
args.extend_from_slice(&["--database-type", "sqlite"]);
assert!(run_bootstrap_start(&ctx.owner_cli_conf_path, &args).is_ok());
// The PKCS12 password is not supplied: start should fail
let mut args: Vec<&str> = vec![
"--https-p12-file",
"test_data/certificates/kmserver.acme.com.p12",
];
assert!(run_bootstrap_start(&ctx.owner_cli_conf_path, &args).is_err());
// The PKCS12 password is supplied start should succeed (in HTTPS mode)
args.extend_from_slice(&["--https-p12-password", "password"]);
assert!(run_bootstrap_start(&ctx.owner_cli_conf_path, &args).is_ok());
Ok(())
}
fn run_bootstrap_start(cli_conf_path: &str, args: &[&str]) -> Result<String, CliError> {
let mut cmd = Command::cargo_bin(PROG_NAME)?;
cmd.env(KMS_CLI_CONF_ENV, cli_conf_path);
cmd.env("RUST_LOG", "cosmian_kms_cli=info");
cmd.arg(SUB_COMMAND).args(args);
let output = recover_cmd_logs(&mut cmd);
if output.status.success() {
let output = std::str::from_utf8(&output.stdout)?;
return Ok(output.to_string())
}
Err(CliError::Default(
std::str::from_utf8(&output.stderr)?.to_owned(),
))
}

View file

@ -395,26 +395,67 @@ async fn import_encrypt_decrypt(curve_name: &str) -> Result<(), CliError> {
}
#[tokio::test]
#[cfg(not(feature = "fips"))]
// P-192 should not be used in FIPS mode. See NIST.SP.800-186 - Section 3.2.1.1.
async fn test_certificate_encrypt_using_prime192() -> Result<(), CliError> {
import_encrypt_decrypt("prime192v1").await
}
#[tokio::test]
#[cfg(feature = "fips")]
async fn test_certificate_encrypt_using_prime224() -> Result<(), CliError> {
let err_check = import_encrypt_decrypt("secp224r1").await;
// Only RSA KEM allowed in FIPS mode. Fix with issue #112.
assert!(err_check.is_err());
Ok(())
}
#[tokio::test]
#[cfg(not(feature = "fips"))]
async fn test_certificate_encrypt_using_prime224() -> Result<(), CliError> {
import_encrypt_decrypt("secp224r1").await
}
#[tokio::test]
#[cfg(not(feature = "fips"))]
// Edwards curve shall be used **for digital signature only**.
// See NIST.SP.800-186 - Section 3.1.2 table 2 and NIST.FIPS.186-5.
async fn test_certificate_encrypt_using_ed25519() -> Result<(), CliError> {
import_encrypt_decrypt("ED25519").await
}
#[tokio::test]
#[cfg(feature = "fips")]
async fn test_certificate_encrypt_using_prime256() -> Result<(), CliError> {
let err_check = import_encrypt_decrypt("prime256v1").await;
// Only RSA KEM allowed in FIPS mode. Fix with issue #112.
assert!(err_check.is_err());
Ok(())
}
#[tokio::test]
#[cfg(not(feature = "fips"))]
async fn test_certificate_encrypt_using_prime256() -> Result<(), CliError> {
import_encrypt_decrypt("prime256v1").await
}
#[tokio::test]
#[cfg(feature = "fips")]
async fn test_certificate_encrypt_using_secp384r1() -> Result<(), CliError> {
let err_check = import_encrypt_decrypt("secp384r1").await;
// Only RSA KEM allowed in FIPS mode. Fix with issue #112.
assert!(err_check.is_err());
Ok(())
}
#[tokio::test]
#[cfg(not(feature = "fips"))]
async fn test_certificate_encrypt_using_secp384r1() -> Result<(), CliError> {
import_encrypt_decrypt("secp384r1").await
}

View file

@ -1,11 +1,9 @@
mod access;
mod auth_tests;
mod bootstrap_server;
mod certificates;
mod cover_crypt;
mod elliptic_curve;
mod new_database;
mod sgx;
mod shared;
mod symmetric;

View file

@ -117,7 +117,7 @@ async fn test_multiple_databases() -> Result<(), CliError> {
let tmp_path = tmp_dir.path();
// init the test server
// since we are going to rewrite the conf, use a different port
let ctx = start_test_server_with_options(9997, true, false, false, false, false).await;
let ctx = start_test_server_with_options(9997, true, false, false, false).await;
// create a symmetric key in the default encrypted database
let key_1 = create_symmetric_key(&ctx.owner_cli_conf_path, None, None, None, &[])?;

View file

@ -1,45 +0,0 @@
use std::process::Command;
use assert_cmd::prelude::*;
use predicates::prelude::*;
use crate::{
config::KMS_CLI_CONF_ENV,
error::CliError,
tests::{
utils::{init_test_server, ONCE},
CONF_PATH, PROG_NAME,
},
};
const SUB_COMMAND: &str = "trust";
#[tokio::test]
pub async fn test_quote() -> Result<(), CliError> {
ONCE.get_or_init(init_test_server).await;
let mut cmd = Command::cargo_bin(PROG_NAME)?;
cmd.env(KMS_CLI_CONF_ENV, CONF_PATH);
cmd.env("RUST_LOG", "cosmian_kms_cli=info");
cmd.arg(SUB_COMMAND)
.args(vec!["--mr-enclave", "dummy", "/tmp"]);
recover_cmd_logs(&mut cmd);
cmd.assert()
.success()
.stdout(predicate::str::contains(
"You can check all these files manually.",
))
.stdout(predicate::str::contains(
"... Remote attestation checking Ok",
))
.stdout(predicate::str::contains("... MR signer checking Ok"))
.stdout(predicate::str::contains("... Quote checking Ok"))
.stdout(predicate::str::contains("... Date checking Ok "))
.stdout(predicate::str::contains(
"... Quote report data (manifest, kms certificates and nonce) checking Ok ",
));
// We do not test: "... MR enclave checking Ok" because we don't know yet how to pass `--mr-enclave` in the CI
Ok(())
}

View file

@ -1 +0,0 @@
//mod integration_tests;

View file

@ -1,9 +1,12 @@
use std::{path::Path, process::Command};
use assert_cmd::prelude::*;
use cosmian_kmip::kmip::{
kmip_data_structures::KeyMaterial,
kmip_types::{CryptographicAlgorithm, KeyFormatType, RecommendedCurve},
use cosmian_kmip::{
kmip::{
kmip_data_structures::KeyMaterial,
kmip_types::{CryptographicAlgorithm, KeyFormatType, RecommendedCurve},
},
openssl::pad_be_bytes,
};
use openssl::pkey::{Id, PKey};
use tempfile::TempDir;
@ -344,7 +347,11 @@ pub async fn test_export_x25519() -> Result<(), CliError> {
_ => panic!("Invalid key value type"),
};
assert_eq!(recommended_curve, &RecommendedCurve::CURVE25519);
let pkey_1 = PKey::private_key_from_raw_bytes(&d.to_bytes_be(), Id::X25519)?;
let mut d_vec = d.to_bytes_be();
// 32 is privkey size on x25519.
pad_be_bytes(&mut d_vec, 32);
println!("dvec size is {:?}", d_vec.len());
let pkey_1 = PKey::private_key_from_raw_bytes(&d_vec, Id::X25519).unwrap();
// Export the bytes only
export_key(
@ -358,11 +365,11 @@ pub async fn test_export_x25519() -> Result<(), CliError> {
false,
)?;
let bytes = read_bytes_from_file(&tmp_path.join("output.export.bytes"))?;
let pkey_2 = PKey::private_key_from_der(&bytes)?;
let pkey_2 = PKey::private_key_from_der(&bytes).unwrap();
assert_eq!(
pkey_1.private_key_to_pkcs8()?,
pkey_2.private_key_to_pkcs8()?
pkey_1.private_key_to_pkcs8().unwrap(),
pkey_2.private_key_to_pkcs8().unwrap()
);
//
@ -399,7 +406,7 @@ pub async fn test_export_x25519() -> Result<(), CliError> {
_ => panic!("Invalid key value type"),
};
assert_eq!(recommended_curve, &RecommendedCurve::CURVE25519);
let pkey_1 = PKey::public_key_from_raw_bytes(q_string, Id::X25519)?;
let pkey_1 = PKey::public_key_from_raw_bytes(q_string, Id::X25519).unwrap();
// Export the bytes only
export_key(
@ -413,9 +420,12 @@ pub async fn test_export_x25519() -> Result<(), CliError> {
false,
)?;
let bytes = read_bytes_from_file(&tmp_path.join("output.export.bytes"))?;
let pkey_2 = PKey::public_key_from_der(&bytes)?;
let pkey_2 = PKey::public_key_from_der(&bytes).unwrap();
assert_eq!(pkey_1.public_key_to_der()?, pkey_2.public_key_to_der()?);
assert_eq!(
pkey_1.public_key_to_der().unwrap(),
pkey_2.public_key_to_der().unwrap()
);
Ok(())
}

View file

@ -7,11 +7,11 @@ use cosmian_kmip::kmip::{
kmip_objects::Object,
kmip_types::{CryptographicAlgorithm, LinkType, UniqueIdentifier, WrappingMethod},
};
use cosmian_kms_utils::crypto::{
curve_25519::operation::create_x25519_key_pair, symmetric::create_symmetric_key,
wrap::decrypt_bytes,
};
#[cfg(not(feature = "fips"))]
use cosmian_kms_utils::crypto::curve_25519::operation::create_x25519_key_pair;
use cosmian_kms_utils::crypto::{symmetric::create_symmetric_key, wrap::decrypt_bytes};
use tempfile::TempDir;
#[cfg(not(feature = "fips"))]
use tracing::debug;
use crate::{
@ -102,6 +102,7 @@ pub async fn test_import_export_wrap_rfc_5649() -> Result<(), CliError> {
Ok(())
}
#[cfg(not(feature = "fips"))]
#[tokio::test]
pub async fn test_import_export_wrap_ecies() -> Result<(), CliError> {
// create a temp dir
@ -110,11 +111,9 @@ pub async fn test_import_export_wrap_ecies() -> Result<(), CliError> {
// init the test server
let ctx = ONCE.get_or_init(start_default_test_kms_server).await;
// Generate a symmetric wrapping key
let mut rng = CsRng::from_entropy();
let wrap_private_key_uid = "wrap_private_key_uid";
let wrap_public_key_uid = "wrap_public_key_uid";
let wrap_key_pair =
create_x25519_key_pair(&mut rng, wrap_private_key_uid, wrap_public_key_uid)?;
let wrap_key_pair = create_x25519_key_pair(wrap_private_key_uid, wrap_public_key_uid)?;
// Write the private key to a file and import it
let wrap_private_key_path = tmp_path.join("wrap.private.key");
write_kmip_object_to_file(wrap_key_pair.private_key(), &wrap_private_key_path)?;

View file

@ -7,7 +7,7 @@ use crate::{
pub async fn test_create_symmetric_key_with_jwe() -> Result<(), CliError> {
// init the test server
// since we are going to rewrite the conf, use a different port
let ctx = start_test_server_with_options(19997, true, false, false, true, false).await;
let ctx = start_test_server_with_options(19997, true, false, false, true).await;
create_symmetric_key(&ctx.owner_cli_conf_path, None, None, None, &["test_jwe"])?;

View file

@ -166,6 +166,8 @@ pub fn password_wrap_import_test(
let key_bytes = object.key_block()?.key_bytes()?;
//wrap and unwrap using a password
// TODO - Remove not fips flag by solving #124 on Github.
#[cfg(not(feature = "fips"))]
{
wrap(
&ctx.owner_cli_conf_path,

View file

@ -12,10 +12,8 @@ use assert_cmd::prelude::{CommandCargoExt, OutputAssertExt};
use base64::{engine::general_purpose::STANDARD as b64, Engine as _};
use cloudproof::reexport::crypto_core::{CsRng, RandomFixedSizeCBytes, SymmetricKey};
use cosmian_kms_server::{
bootstrap_server::{start_https_bootstrap_server, BootstrapServerMessage},
config::{
BootstrapServerConfig, ClapConfig, DBConfig, HttpConfig, HttpParams, JWEConfig, Jwk,
JwtAuthConfig, ServerParams,
ClapConfig, DBConfig, HttpConfig, HttpParams, JWEConfig, Jwk, JwtAuthConfig, ServerParams,
},
kms_server::start_kms_server,
};
@ -91,17 +89,16 @@ impl TestsContext {
/// Start a test KMS server in a thread with the default options:
/// JWT authentication and encrypted database, no TLS
pub async fn start_default_test_kms_server() -> TestsContext {
start_test_server_with_options(9990, false, true, true, false, false).await
start_test_server_with_options(9990, false, true, true, false).await
}
/// Start a server (KMS or bootstrap) in a thread with the given options
/// Start a KMS server in a thread with the given options
pub async fn start_test_server_with_options(
port: u16,
use_jwt_token: bool,
use_https: bool,
use_client_cert: bool,
use_jwe_encryption: bool,
use_bootstrap_server: bool,
) -> TestsContext {
let server_params = genererate_server_params(
port,
@ -109,7 +106,6 @@ pub async fn start_test_server_with_options(
use_https,
use_client_cert,
use_jwe_encryption,
use_bootstrap_server,
)
.await
.unwrap();
@ -117,65 +113,38 @@ pub async fn start_test_server_with_options(
// Create a (object owner) conf
let (owner_cli_conf_path, mut owner_cli_conf) = generate_owner_conf(&server_params).unwrap();
// Start the server (bootstrap or KMS) in a thread and wait for it to be up
if server_params.bootstrap_server_params.use_bootstrap_server {
println!(
"Starting bootstrap server at URL: {} with server params {:?}",
owner_cli_conf
.bootstrap_server_url
.as_ref()
.expect("The bootstrap server URL should be configured"),
&server_params
);
println!(
"Starting KMS test server at URL: {} with server params {:?}",
owner_cli_conf.kms_server_url, &server_params
);
let (server_handle, thread_handle) =
start_test_bootstrap_server(server_params).expect("Can't start bootstrap server");
let (server_handle, thread_handle) =
start_test_kms_server(server_params).expect("Can't start KMS server");
// generate a user conf
let user_cli_conf_path =
generate_user_conf(port, &owner_cli_conf).expect("Can't generate user conf");
// wait for the server to be up
wait_for_server_to_start(&owner_cli_conf_path)
.await
.expect("server timeout");
TestsContext {
owner_cli_conf_path,
user_cli_conf_path,
owner_cli_conf,
server_handle,
thread_handle,
}
} else {
println!(
"Starting KMS test server at URL: {} with server params {:?}",
owner_cli_conf.kms_server_url, &server_params
);
// Configure a database and create the kms json file
let database_secret =
create_new_database(&owner_cli_conf_path).expect("failed configuring a database");
let (server_handle, thread_handle) =
start_test_kms_server(server_params).expect("Can't start KMS server");
// Rewrite the conf with the correct database secret
owner_cli_conf.kms_database_secret = Some(database_secret);
write_json_object_to_file(&owner_cli_conf, &owner_cli_conf_path)
.expect("Can't write owner CLI conf path");
// wait for the server to be up
wait_for_server_to_start(&owner_cli_conf_path)
.await
.expect("server timeout");
// generate a user conf
let user_cli_conf_path =
generate_user_conf(port, &owner_cli_conf).expect("Can't generate user conf");
// Configure a database and create the kms json file
let database_secret =
create_new_database(&owner_cli_conf_path).expect("failed configuring a database");
// Rewrite the conf with the correct database secret
owner_cli_conf.kms_database_secret = Some(database_secret);
write_json_object_to_file(&owner_cli_conf, &owner_cli_conf_path)
.expect("Can't write owner CLI conf path");
// generate a user conf
let user_cli_conf_path =
generate_user_conf(port, &owner_cli_conf).expect("Can't generate user conf");
TestsContext {
owner_cli_conf_path,
user_cli_conf_path,
owner_cli_conf,
server_handle,
thread_handle,
}
TestsContext {
owner_cli_conf_path,
user_cli_conf_path,
owner_cli_conf,
server_handle,
thread_handle,
}
}
@ -198,28 +167,6 @@ fn start_test_kms_server(
Ok((server_handle, thread_handle))
}
/// Start a test bootstrap server with the given config in a separate thread
fn start_test_bootstrap_server(
server_params: ServerParams,
) -> Result<(ServerHandle, JoinHandle<Result<(), CliError>>), CliError> {
let (tx, rx) = mpsc::channel::<ServerHandle>();
let tokio_handle = tokio::runtime::Handle::current();
let thread_handle = thread::spawn(move || {
// we instantiate the message channel in the thread so that it is kept alive
// but we ignore the messages sent by the bootstrap server
let (bs_msg_tx, _bs_msg_rx) = mpsc::channel::<BootstrapServerMessage>();
tokio_handle
.block_on(start_https_bootstrap_server(server_params, tx, bs_msg_tx))
.map_err(|e| CliError::ServerError(e.to_string()))
});
trace!("Waiting for test bootstrap server to start...");
let server_handle = rx
.recv_timeout(Duration::from_secs(25))
.expect("Can't get test bootstrap server handle after 25 seconds");
trace!("... got handle ...");
Ok((server_handle, thread_handle))
}
/// Create a new database and return the database secret
pub fn fetch_version(cli_conf_path: &str) -> Result<String, CliError> {
// Configure a database and create the kms json file
@ -288,7 +235,6 @@ async fn genererate_server_params(
use_https: bool,
use_client_cert: bool,
use_jwe_encryption: bool,
use_bootstrap_server: bool,
) -> Result<ServerParams, CliError> {
let jwk_private_key: Option<Jwk> = if use_jwe_encryption {
Some(JWE_PRIVATE_KEY_JSON.parse().expect("Wrong JWK private key"))
@ -304,11 +250,7 @@ async fn genererate_server_params(
JwtAuthConfig::default()
},
db: DBConfig {
database_type: if use_bootstrap_server {
None
} else {
Some("sqlite-enc".to_string())
},
database_type: Some("sqlite-enc".to_string()),
clear_database: true,
..Default::default()
},
@ -342,11 +284,6 @@ async fn genererate_server_params(
jwe: JWEConfig {
jwk_private_key: jwk_private_key.clone(),
},
bootstrap_server: BootstrapServerConfig {
use_bootstrap_server,
bootstrap_server_port: port,
..Default::default()
},
..Default::default()
};
ServerParams::try_from(&clap_config)
@ -372,12 +309,12 @@ fn generate_owner_conf(server_params: &ServerParams) -> Result<(String, CliConf)
} else {
None
},
ssl_client_pkcs12_path: if server_params.verify_cert.is_some() {
ssl_client_pkcs12_path: if server_params.client_cert.is_some() {
Some("test_data/certificates/owner.client.acme.com.p12".to_string())
} else {
None
},
ssl_client_pkcs12_password: if server_params.verify_cert.is_some() {
ssl_client_pkcs12_password: if server_params.client_cert.is_some() {
Some("password".to_string())
} else {
None
@ -387,14 +324,6 @@ fn generate_owner_conf(server_params: &ServerParams) -> Result<(String, CliConf)
} else {
None
},
bootstrap_server_url: if server_params.bootstrap_server_params.use_bootstrap_server {
Some(format!(
"https://0.0.0.0:{}",
server_params.bootstrap_server_params.bootstrap_server_port
))
} else {
None
},
// We use the private key since the private key is the public key with additional information.
..Default::default()
};

View file

@ -1,20 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDMDCCAhgCCQDAPL/cC9h3iTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJG
UjEMMAoGA1UECAwDSWRGMQ4wDAYDVQQHDAVQYXJpczERMA8GA1UECgwIQWNtZVRl
c3QxGjAYBgNVBAMMEUFjbWUgVGVzdCBSb290IENBMB4XDTIzMDUxOTE1Mjk0NVoX
DTMzMDUxNjE1Mjk0NVowWjELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lkRjEOMAwG
A1UEBwwFUGFyaXMxETAPBgNVBAoMCEFjbWVUZXN0MRowGAYDVQQDDBFBY21lIFRl
c3QgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANYf+HTZ
AXLZaWj43qRTqdKs0piG6sk0oY7RFyE2/+KE6nArFPo70pAHgrzkiabPL8gGPxyL
wTxS+So7crRKNDeGsaHcejxJyh6ySq/4CfWuO7ZBIFU7m0Di4QSWbJyMDPLevaFn
+oBL2x+8Zs2oIFL5kYdx/eMY105IlNUbgIoGoQMww+mF7CsAZ8YPlVrXTWT9Rmer
p22dwX9oGzY5Fpi/KjMDZlwZ4sIKnK3RdKSf0jbonnVB86rNyEU+zglNHIhnYaOc
u/gJ9fJzluHJz0MSgMB2cOh6O8ye1sOAN/U+XgBv4tlS1I2bV9dlBhpfjCyjDM45
TdLtiOtoVmIyORkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAxIxqxn/YAGKBbgZW
N7ZLF9iCEpmocYzwTn05mMdh5ywwMPJ908lVdUv+JfPXUNFcar/8iH/WOyehw0W4
+ceLqIYZaGbEkYDDuje/INkYsOrWBRwa6rPJh5jENYCqEOeQn39/Ei6TScx0/m78
+UfHYZRcO6v2iXN3PMezb780bU1ChruIu0X6U4iHD1i9GS9u1rhUHCacf0131i3o
YOmBzbbV6ZV5FkwEjICJi/a1PnMauXmX/0jaX8i7Y1dXTNtYvmki7KnNZE50Qhd7
ZgMmZdGGqfYCfs9ilSNZIu8EUoXZEYGC4S1gjqoh5YebCLO8qJTRDpR2JQWLkHlv
x/zdVw==
MIIDlTCCAn2gAwIBAgIUfDRrRInqLZKVabmBD68XsqmlpPgwDQYJKoZIhvcNAQEL
BQAwWjELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lkRjEOMAwGA1UEBwwFUGFyaXMx
ETAPBgNVBAoMCEFjbWVUZXN0MRowGAYDVQQDDBFBY21lIFRlc3QgUm9vdCBDQTAe
Fw0yMzEyMDYxMzI4NTJaFw0zMzEyMDMxMzI4NTJaMFoxCzAJBgNVBAYTAkZSMQww
CgYDVQQIDANJZEYxDjAMBgNVBAcMBVBhcmlzMREwDwYDVQQKDAhBY21lVGVzdDEa
MBgGA1UEAwwRQWNtZSBUZXN0IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQCo6TwveDxbHYNEGgDnTQHtfPFFKbMsoJpD1emGwNMLsFJgfXLo
mLPnv690cbiE44aEAFl6cjLoizluhT0Ue03I2uflOT+JCeF3HSBLdd+VQcmSgWQD
Mnawd4j7H83KinWtWZaYK/MntYrMExKNt7RoaCZjtOo8RU3G+gY3aK8l5U3TUMHQ
kplaNXVMHfTnrAAaTh2RzlfNF6Hy7bgvkncnwuyMeZHzbfxtJ/XBp20cIQL50T48
OOzzN1jiTsIMnDndGqT8/AcJstjC6seXG7RPb+O1iP7eQwLIKEAfLls4p6KsQW+A
MI3oxKJviOxpyk/623B6rQ30G2Q3okZnipqtAgMBAAGjUzBRMB0GA1UdDgQWBBQG
UrI84vawMQ/sK+qhnG6PvW1RDDAfBgNVHSMEGDAWgBQGUrI84vawMQ/sK+qhnG6P
vW1RDDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCa2m7/d31p
bwwLaD7ij5AWDYH7tAEw8ay3GRYj2Cx8VfxXUrsQLb2pnrFGZKnjIcyrDTfJZ/tM
mSZSiplhjrYZ+ubs4+Hxzslzi5Ouu86VJIdiPTCp4T+pQR0RMGs4Dc71r4zYz8pG
ym4C5umQI1SkwnIJxPkVSu7FQxt/dzzMUALblvEFa9F0Fl0OzZlgXGXIcrOaAayW
yCQuhfhMHnWo4ogh4maPnWiSR4vnG0FIMcM8oXPMO2kL7doUk84fAZJu5gglOAPW
kUWxJiaKo2oX5k/yILww9MC5MptMJ51mHk9O4IKE+PXJ0Jbc6UlTzPOkwT6aoo8p
kwpPcggYgoUl
-----END CERTIFICATE-----

View file

@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDWH/h02QFy2Wlo
+N6kU6nSrNKYhurJNKGO0RchNv/ihOpwKxT6O9KQB4K85Immzy/IBj8ci8E8Uvkq
O3K0SjQ3hrGh3Ho8Scoeskqv+An1rju2QSBVO5tA4uEElmycjAzy3r2hZ/qAS9sf
vGbNqCBS+ZGHcf3jGNdOSJTVG4CKBqEDMMPphewrAGfGD5Va101k/UZnq6dtncF/
aBs2ORaYvyozA2ZcGeLCCpyt0XSkn9I26J51QfOqzchFPs4JTRyIZ2GjnLv4CfXy
c5bhyc9DEoDAdnDoejvMntbDgDf1Pl4Ab+LZUtSNm1fXZQYaX4wsowzOOU3S7Yjr
aFZiMjkZAgMBAAECggEAWInxt4c/tRi3IIO/tB0wHH//uLgY/0e3AhHtSXuvhR/G
vMjjrLDt2UZxxd+OF56WOFR05j3XkjUQ+GiKfogsXUVhz/cVyDYO0HCSt36Nx7va
zvrQ/s1d3g+yUa5NjNHQTODTcplHGKPFILNLowBBFW5Cwcnd3HiaFrGmcRQlK2zd
+cie78jiy8BVm6oOS7Bi+FeagVDOolPLm1ZrpLW65iu3hzz59nrpTezRck/LrDD3
LU6HLMxtGY0cjaHreR3aXaTM6CZyESw1tdo+LXuvTtNwU9DENd6lbYuCrOmDOkxB
IRkTuSOdsn+swWRoP27tqEvwBK/QG5fp5E2GXaMMzQKBgQD+s7ATmXS6T8HqaGX6
Xkwlhmtb1PAXzrbhMVqCrADsooWmLraW8rKQjA6YkSu7UfD9TkXMGto1M2Z9lo4l
LQ0xVvW2CL4y395YHpo35bWckhYuqn8m8WOkI9p3SDhTnIMOMymfduE2h+3JWcAn
jottsG9+8sv6EOdUwZD7EdwFewKBgQDXN1drCQTmH9/KiYIst0XbXxa/8yTppdnG
J5+DEOZF3mTXfVGbd/s0PNKiqDQKbwbh2SygsCEyKYC4vZLmq7MDtmNL5kLeTvzf
6Flk9SpEj1kKOxAWLlgc57ivZdqGSUM9Aj2v6tMeo84eEp6D1tDjLP5RdSNjgIAv
rwAACXGVewKBgQClI+Q6EPk6A5R7Y40bNCCzA+B//iRdi03P3KwOpW9D/mwhP5Br
dpiIWAburNFp9ssFscZXe+GXNOhy7Tbkq70uDG/rwudvHO+QuubK70k9Zwqy3yDq
IwCz3/s5871xmLzwbAPEvNNxA9kNAAAypZ2JVSg1az8NuAAFWCukXgQGGQKBgQCP
klO3C+VAv7LDosg8nGb12ZGLq+DMHeAR2Q0ImpWDtsD/IJL4bCogxxKdgCh3bWnh
8MdcyyLaG+XLWGxPhet+ZoYHdCzXsUnw9UftmcAAzMBRmuU4ZuJRJiSGniQRwX4h
jQUp/jWpEw3F8hXdTck8RB/Ep6hcELVzGgOeAq7LUQKBgEXSfUdMw1dAXbl4DUGi
qJYIABJfTp8xp4Pgo3tHYTtIPBkqm6BoqPa0UsiRwLJm8+t/5iO3/nFv/9xL2wBg
rq5678GY52Kn5XfZ7ubEYRD/Fuga7Et03JN3m7AdIS6I0I0sFh9T1dF2WljfrZ8m
V6h8iJs3X6F+7MGcID9i0l8F
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCo6TwveDxbHYNE
GgDnTQHtfPFFKbMsoJpD1emGwNMLsFJgfXLomLPnv690cbiE44aEAFl6cjLoizlu
hT0Ue03I2uflOT+JCeF3HSBLdd+VQcmSgWQDMnawd4j7H83KinWtWZaYK/MntYrM
ExKNt7RoaCZjtOo8RU3G+gY3aK8l5U3TUMHQkplaNXVMHfTnrAAaTh2RzlfNF6Hy
7bgvkncnwuyMeZHzbfxtJ/XBp20cIQL50T48OOzzN1jiTsIMnDndGqT8/AcJstjC
6seXG7RPb+O1iP7eQwLIKEAfLls4p6KsQW+AMI3oxKJviOxpyk/623B6rQ30G2Q3
okZnipqtAgMBAAECggEAKZLig+KJpHmCngfycfvDvjLS5Y2KcrEC1zB23npmV4mp
9OLbngE7wo4s/rGg02B28tVtMJScEB14YMn9iyWAMzAiBIrPJwkeC+XI1ZnpEoET
PnKHbbwFd5CsT/b3fWMP7L9QsBqdKghmZa1KE0SEbHA6Bq94OaZrKs7OKeQju+Uq
PaSDnCumAPDUKdgSthnUOywgaCzAufnnmQoxhFlbtOBfO8C1fbCCgLp2KK6bT/iY
AVI3IxETmM0bFFHcxyt/KoN7KubnzBvWkRPHiCWfA91u4YTvYBi22dpnIjaglXIu
FYek0cCYUkvQm9YOb7Da57qPPT3s0SNEtjQIgOG5dwKBgQC+h+dC9Chj9/ydVcpM
KIpCvf/2eWQOjNccD8wUdB1rlbI72JJiceaYLCFdcjMsAO3Ke6C75qReBcJkTOfS
rQ1IbuD2luTzWkQTAosQ1HS0h5oWa0zXlDj7GJtBZh2je6cv4O1qPEkX4OCGmfOE
AxVHBBsD/jgAsefMifL7IuJ0RwKBgQDi84psRg2Z3+dUraDB6BfSwQm3tgXsHGoM
ENN335CQWQyJ0WIvutJSRt3fdrayTT/WkKS40D2NpuSldk3+MBvmaIaEAqjyrJ8Y
dEr6MAWX0BSrUd8jSAm+xHTI4VUrr/nMVST9leaWRgm5NE/2PYjJUE1EfNHUgy6i
pTzbKPF3awKBgGn2a0dxQoVWhcd6zudMJJi50oDhQiRVb8zLfFRjv3j16AogiEj3
z6cR4/x0ZsEyQw05dEsMGp2v1kyx+LvSQnDmOXbqtKoiqXlpEK5refxs7eVKB5Jd
fNMo/C+C+zjQrEqUbXWH0Z7WEgjJ1gS1MkAsqFmwKLU6PBAlKF1DKW5BAoGAOmVN
5cQ0tbYuENTIO3ybvKfc9z9H0NQS2V3u+7YzvWCcYE3XBM7+VRtU9a3XHpPP7Ea1
i7RRUkRyKr0BHnmEhEm66vbAQAj4utMZg7ydg82Ps/FSCAL+Nu7X2eRZBO+3x8LU
zMcoWNxWnif+ty5oJtyZtRik6RiEBGed/ApR71cCgYAp/rTtwNS02Y5gxjSSZYON
M3LLJQO4iCZrhY4fZOIcZTm7Se6ouIsW5DOd3YavMb9VcKpwNCn3ZybjOEPDSTVR
WTyf48pVIXJesMyno557A+RtYXbN5pYSkbI+CUUNKjll6Rt1Bs/tEBSXKkc6GO+5
V1xAZvTW1jK3LpqH/QuMyQ==
-----END PRIVATE KEY-----

View file

@ -1,20 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDMDCCAhgCCQDTJGaQvJLD0DANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJG
UjEMMAoGA1UECAwDSWRGMQ4wDAYDVQQHDAVQYXJpczERMA8GA1UECgwIQWNtZVRl
c3QxGjAYBgNVBAMMEUFjbWUgVGVzdCBSb290IENBMB4XDTIzMDUxOTE1Mjk0NVoX
DTMzMDUxNjE1Mjk0NVowWjELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lkRjEOMAwG
A1UEBwwFUGFyaXMxETAPBgNVBAoMCEFjbWVUZXN0MRowGAYDVQQDDBFrbXNlcnZl
ci5hY21lLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKXnnzdx
N48j28FJO+HNpeswAZSriNQGAtvd3fiak/MeZ6VbIjXgGIZ073r9aNYsOxkIR/aW
HVBDcQBsQqUfP+Ni9E1Qfw/Uav1CqnZszAaX6BUJ1bRzT0WfYMgnjiNnqJPf2FAC
XvdifjB8fiEBfCBUZ8BuiqTYWVTnBLC33oLpJAn57oUgCUnbNS6WNCQfmO5sIFdd
E3VwfOxeOxFsJ5pCVSJu12zJACJ6nNjbAt3jdVBnix6tN8iuQM8jlKk0NgGqRu9F
VxZ0/USwSnTW2bXKmk2xgVF1SxdNC5EWK0H7PbF+P5CSWK17KtAVTGQhH9BI6EcZ
8Z8hDewPDECnXBcCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAsNO3wkHT6M5qG7l7
4YL+4K+dm8TB6waUAmGIuDcnQ1uO1iSIkYl9RQG545O7WjN9qozvLg45lBdecLAQ
27J8F/tOnIw6hpTuUdBN4NGNH5RN5z6rsjZLevuRHulRAgHGUMnmBGb9jDiIm63f
e0YF++xS3mW4viCFRY8ECJ/43XNLxejp7ZAZUkWaJnRhTi7iSNd00ENjKwSsX0UC
KlhwB2Jkrs7QDS6OAzAb05r+VjeBkgpBaimyl6mHSrrbUz086A/vn6U3l33QiTVP
EQwLQ3B8ubLLPy8hs/RVWubr8+DZ2lRwy2cdzqKhZT816uWV1Kk4TsPfLl2FgQ+G
JfbZkg==
MIIDOzCCAiMCFHvc/pbd2xt7p9gk+cPUiP6O+TL+MA0GCSqGSIb3DQEBCwUAMFox
CzAJBgNVBAYTAkZSMQwwCgYDVQQIDANJZEYxDjAMBgNVBAcMBVBhcmlzMREwDwYD
VQQKDAhBY21lVGVzdDEaMBgGA1UEAwwRQWNtZSBUZXN0IFJvb3QgQ0EwHhcNMjMx
MjA2MTMyODUyWhcNMzMxMjAzMTMyODUyWjBaMQswCQYDVQQGEwJGUjEMMAoGA1UE
CAwDSWRGMQ4wDAYDVQQHDAVQYXJpczERMA8GA1UECgwIQWNtZVRlc3QxGjAYBgNV
BAMMEWttc2VydmVyLmFjbWUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEAi5cvrpnkRBNPl9KZvpfDpZEbyiPBYymXFw2tAoAVozy6lXwHGJMGROY7
ML28gOLiZZdQ1VQDjsip1KxwQX4IJsXWbLTEV5qj267xfhsjgaeXWVjdllY7EYK7
AjtkWOuNw+unqQDdTRpKovd5/dDHZJjOBXJWPjFu8YMug2ua9pLQzad2D6Eg2bkr
Hp0DAXipDZC84DAEmbJyrZrSkVKiy/A9HEzUjv+2zv3yXHIAXAGJp3XHMrNxiORY
HYFKpawhNrPfzXCLCtYkAtWPmU3d+jNkbeY0QPiu24vbfkZnX+kXSmJxTDfzFE0m
1JrgHOzB7yxGCeKPdaJMlkaTIIcxnQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAM
WLbwGpbzZC1lH1PudMTB2dEsjgjZISb5nP/DnKToIqoVhVt7k8vBaos5jNKROGl6
wvv7e/+5mcvBqJp/uIw1vkMr9sIf7wYk9tlOrUlOkPH/7f9yvcPBpcjUfMXiiMzG
aF9o+kXfrMcA8olSczcaodA7soRqQq4a1aBYl29Z1U+VoqjpdoD4KKCfOskwH3vQ
5Q+xfQ04G6alRss5sSuU107osyCGtvJygcagtGkr/xr2ZlOhZnsJNLKwidm9Nshs
MAKl65UTYM3j/5JfCF1/9kRFfZiwmlH2rGWsTqRtjka4XkVy47qnW5Q0iFDzHgKE
N2PGxEjdRC6CRAuQMs9+
-----END CERTIFICATE-----

View file

@ -1,17 +1,17 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICnzCCAYcCAQAwWjELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lkRjEOMAwGA1UE
BwwFUGFyaXMxETAPBgNVBAoMCEFjbWVUZXN0MRowGAYDVQQDDBFrbXNlcnZlci5h
Y21lLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKXnnzdxN48j
28FJO+HNpeswAZSriNQGAtvd3fiak/MeZ6VbIjXgGIZ073r9aNYsOxkIR/aWHVBD
cQBsQqUfP+Ni9E1Qfw/Uav1CqnZszAaX6BUJ1bRzT0WfYMgnjiNnqJPf2FACXvdi
fjB8fiEBfCBUZ8BuiqTYWVTnBLC33oLpJAn57oUgCUnbNS6WNCQfmO5sIFddE3Vw
fOxeOxFsJ5pCVSJu12zJACJ6nNjbAt3jdVBnix6tN8iuQM8jlKk0NgGqRu9FVxZ0
/USwSnTW2bXKmk2xgVF1SxdNC5EWK0H7PbF+P5CSWK17KtAVTGQhH9BI6EcZ8Z8h
DewPDECnXBcCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQCYqz562+BqLIYo6nqU
EOK4EsPmAmaDmHbosk6c3qADSvd8RjLyPHxQRvgJtbOZD7zAEdSdO6hnejU0D4Cw
hYi4pQ6z73UwnEtLNdmXUmDYBl24g1iVKktgcJbxjXBuK5cMmEAiYxF/ZR4MKapi
2gtDwSrfLNM87zbBy9N3Cc6Yasj0DHq5dUyjm92PjYSw/mzwegK1feZJ4GfUVN6+
FQ8jxQskthPtIm55pTox7XMhQbKTNms8UT6LRb/yY0XZq5jtci7cxEjv5gRmWU/I
TigNDgVHOsRzVwbQhM8mPe7rK6xtJJeyDGolzUY2mLwkPIUlnLjS7O+6SJt9L3ZR
S2Sz
Y21lLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIuXL66Z5EQT
T5fSmb6Xw6WRG8ojwWMplxcNrQKAFaM8upV8BxiTBkTmOzC9vIDi4mWXUNVUA47I
qdSscEF+CCbF1my0xFeao9uu8X4bI4Gnl1lY3ZZWOxGCuwI7ZFjrjcPrp6kA3U0a
SqL3ef3Qx2SYzgVyVj4xbvGDLoNrmvaS0M2ndg+hINm5Kx6dAwF4qQ2QvOAwBJmy
cq2a0pFSosvwPRxM1I7/ts798lxyAFwBiad1xzKzcYjkWB2BSqWsITaz381wiwrW
JALVj5lN3fozZG3mNED4rtuL235GZ1/pF0picUw38xRNJtSa4Bzswe8sRgnij3Wi
TJZGkyCHMZ0CAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQAKJxvuFTtG1/ZU4EnS
6dTnhE800F1hhzSk89T0/zv4Fkv8HzvEKU897e7M8yhPzUDo9NZoEb1GiHiDWjiM
mTALy25S2nfFFOghnMhhXnpel+z16MoC7vsjqdjAF4DF6cJ0Qx8OV1hG82NumEd5
s4UGJbrAd5tpGZ+jddmOoxm+hltqW7OChxWrzm7Vh25n70wjP/SaN9joOhlIxiGH
I4Hr4Bqx2ljcadWFHV1u12szrOsvJtEmYZtQHuvht/N1BAyytRKNZiFMjlSgdPLF
nQ4yeJ49RN+E7OYfuvyspmMjbVe/0o80fk1d3TOKn5YTev2S8G+7psDRZlHwrU/o
f5pM
-----END CERTIFICATE REQUEST-----

View file

@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCl5583cTePI9vB
STvhzaXrMAGUq4jUBgLb3d34mpPzHmelWyI14BiGdO96/WjWLDsZCEf2lh1QQ3EA
bEKlHz/jYvRNUH8P1Gr9Qqp2bMwGl+gVCdW0c09Fn2DIJ44jZ6iT39hQAl73Yn4w
fH4hAXwgVGfAboqk2FlU5wSwt96C6SQJ+e6FIAlJ2zUuljQkH5jubCBXXRN1cHzs
XjsRbCeaQlUibtdsyQAiepzY2wLd43VQZ4serTfIrkDPI5SpNDYBqkbvRVcWdP1E
sEp01tm1yppNsYFRdUsXTQuRFitB+z2xfj+QkliteyrQFUxkIR/QSOhHGfGfIQ3s
DwxAp1wXAgMBAAECggEAXX6jfnYiTltx64Je6cif+WNrcKIekQX2NHvbLM/IEtmO
pWQvalco2xUpJJGMtC3m/Btrmq5MRMBM/WUFRdLPiVBpxuxXPs+Bn8ojEGwNqqWO
19RcB7537Q99Vi7cbl1rZjJez/AWHqn13VM7LVSN5BZNBegF4BJAfZVXUlO8ZN8f
6hxy3hBtAxQgNlGL9xemz1hjNWrVcelKXyAsbff6OpjmJDpTcGoRqopFbABzFAqU
G9jPG6/C6tG/R3YDYK/YRsJLHHEMEaXTi+U35DY+XHJyFPc3YeAZzfVZKo44gD5+
mSTw/g1XP8IUT36zUV8tIxfc+XCc2No+iAkL7Wl7GQKBgQDSG28bajXtpxPoSNQt
2Hx6qp8uUH1uZfxtM+jpjjJTqPbnVZw68xFR4EKBrePEC8D+Q92vOzY35GIZtHXN
sRY2+6RahWMo2SACZNKu58BFXzDDvVIpgSye2Ys7RnmGdN5AeKQgSm+D/E0cDxTe
nYaEXzAbcTl1vuEor1fvmIk3QwKBgQDKJIWln8uxL45EJ2E7SDj9vEgybbHbPtTw
y96ahVLEFhfKkCX257y564xp8vQXTRZh57D+jKRjvE/or1RAYEEI90v3hs604rCZ
S4/BPAtlH41yq/NgQy765i8PR0SrTLqDzJCbJXVzrbY/JwMVuY40CUb9sXuLu2Fm
R0Zbo8oonQKBgBNWkxNi4zPMfPiUO3M8ybhAnAYXQnQ2ztT+QiG9BMTOeGsyZf7H
AIEYswZciuowasL8XBsUyBi83IqhIXadJf5JiwcJ3+aSlW+i+AFBM2EvMb3SXw2S
5hh4zWfXRLgraYllkTWnpDvxrOc4PCOBPDBS0oq0ESXVO8QS33UeBQMhAoGBAI9P
i0zb2G57F7Kr9Jwx3O3PEaRm7sRQbGl3MA9+3CnWu/FJdxDwRHXPUFJBA9qNNYQd
3fbpwRZQiJqfyuyFETaJFNudQGkvmiJkVruZlT6ROxgEXlxt5R3OGakMTtvfXxBX
9GF6EiX8DwwA3YgGcJHlzeXIcZ2kMC5x86i+m3FlAoGAYbkjhKImWbiBOgKO8of2
UZes4rFbtuDHETYydT8sBtHlMr97waT9NMSOWZpii+G1QavrjHISUDyBMYb16heo
50Q4JjWPH4rCC4ZW9MR9ZxWz08HpRfw3NILTYuoz2EpxDUg6/Bo6QcWu7uYizZtZ
SshHT3/UcgNshMetjpwveqQ=
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCLly+umeREE0+X
0pm+l8OlkRvKI8FjKZcXDa0CgBWjPLqVfAcYkwZE5jswvbyA4uJll1DVVAOOyKnU
rHBBfggmxdZstMRXmqPbrvF+GyOBp5dZWN2WVjsRgrsCO2RY643D66epAN1NGkqi
93n90MdkmM4FclY+MW7xgy6Da5r2ktDNp3YPoSDZuSsenQMBeKkNkLzgMASZsnKt
mtKRUqLL8D0cTNSO/7bO/fJccgBcAYmndccys3GI5FgdgUqlrCE2s9/NcIsK1iQC
1Y+ZTd36M2Rt5jRA+K7bi9t+Rmdf6RdKYnFMN/MUTSbUmuAc7MHvLEYJ4o91okyW
RpMghzGdAgMBAAECggEAG/u48VWkbwk4AYWUkKK38+79FS7Jy+lAgryZHFE1BT5d
YS419fUzYoiNAR5oNaZGIHuPFtT6yZrsgNf4wQhU9gDfFXe/jJhRlO6jNtaV7Z39
N0obUdIDkWUVvELPwQunpckOGVE1DAnjQHxAFmPONRp04p6jL2CVjxDvOCVQMXG9
YcebtLfeUtjbCRjYV2hLwoBJFHRd/mRluSzhhpgI/V4AG0qEVyqg2UGht78p+NGd
AgGvsn3uYKHtvsz3fG6R+7tMvnjpsKULHHJDAS6upP1mMaSJksdvG4xW1T+xpUmd
XLxDc3pFo/qsTuUiSd9iFvqIvN3AqM4ewqBvDkwYAQKBgQC6xxhPQ7wMvMUhVt3I
/BsqqmMyxQ9pnI1fXap6TYZyI+61UI51aT0by2tc2NRkmz13zLyu/qxvd5PIdGhe
KUtxmf8hDUZaTQJq0CQMBvKmLqLZ5q4wIRMzF17/D7PxXyj1mbwgu8ZGYdWRRW5U
SYxZv+ARKM33GDjC0FUBIq9onQKBgQC/Ux4lWSI+HByl8AuU5W+Dtf1/GgwFIX9A
fOZG0Ot96IXE4Iq+DEEa10DX0Na+yca5H//zD3raCJRJB/BPe9S7uejDFLMNmS4a
DPtV6JrmWjKFKzux1NGcSbNNpoYq7QG0/kl7eeybJ8N0eztVA1E1g/gm3ILkYuRY
EhvIL5YdAQKBgQCjGO/1Z2RyivRo5H3O37apTxhIYSPQSVB6EkSnf8MDMLVlxu5f
QIKIHt2lugHdyGGolzO8a88Plw+JX30znEOw85SBvCHPAKg3tYGErxx8WQUD2hgJ
Fxi46JOfjorHHx2ZOaG5w76j/xKLRGHPYFoalR6IXWVde9004M8ZqrwZUQKBgEFm
Jg3aNAxLC7flH/BbpQy+rtI9kxJF6vueNhDK5VR6oQ81OffQtIh6P94FswIwcs9h
EtqA1hulhxqXrhtdVtB8sgXNE494ZvvcqCUrh5dqCY7fwl39Q1FLGBFY9M/DiCvY
KEIe4TQAGZ29agCbS60hWTffdJag/zDjr790FLoBAoGAWotPJORbzfNJqA+tp4Qf
/n4FUq0aiyEJLwUFoo+nbqAQaYv0oDpz7hapgXiv8rsoTr6l+ENi3n+yclDFGUAg
zY/2cam9xcDI+ugwnqYo0sx/3LYdWULACJ+ayBTRrQNFTE8V2Z7fQ4OSIMunm40P
kCYoIRGbQttPddxhZqtLD10=
-----END PRIVATE KEY-----

View file

@ -1,20 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDNDCCAhwCCQDTJGaQvJLD0TANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJG
UjEMMAoGA1UECAwDSWRGMQ4wDAYDVQQHDAVQYXJpczERMA8GA1UECgwIQWNtZVRl
c3QxGjAYBgNVBAMMEUFjbWUgVGVzdCBSb290IENBMB4XDTIzMDUxOTE1Mjk0NVoX
DTMzMDUxNjE1Mjk0NVowXjELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lkRjEOMAwG
A1UEBwwFUGFyaXMxETAPBgNVBAoMCEFjbWVUZXN0MR4wHAYDVQQDDBVvd25lci5j
bGllbnRAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCU
x6zAfpMuhi1mBkD1WJdLMEltK19rE8av/PZHZMkt57W9vL1V80HU1OLoc28t8UTj
sGGflZ9XDDZltpQ6Z63cjEuDMAtILzqHCzLCAjziu/9M9KCpLtuRJDI3aw5Awm88
2szH8yTQ2M+sxxGpnDUgKUqDSAYTfZDtUbl+9sr/uXxPVn2gYxvumOeYyKEla4Ix
vI8QLQk+QozB7uY16CeVmB33v1dl+fnjU9wq4Q0aHaD3oVr0spfrQgrkrWdz7O7U
GmrhMROmm0IEBWQWIYCBTM2BiuYkzxrv6ywJqhzpCpsordFB49YVd5JleuYsObRH
4QUMd4Vr2iJamUnsChjdAgMBAAEwDQYJKoZIhvcNAQELBQADggEBACmpFxOcZSUM
lvHcUw21waP8Lad1G+XUHGLcddWLysBYzlIDVqSIvLfEqA1m1i8wtwsvfxPUIgRr
wZXEhn77YHsElisTz6ejthivYwJ2kE4qkbPndi0Vaq7AX+K3V7cw37KEoueIpAMg
uuW+m+BuWg7feyWs6nYO8eZbdWtRZ9BddPWZb84BATeYBcuAhZ6PBX89QmqldBHw
2qu96PS11ZbIenPOQwh4M/BHkbScLaJqRPdg1/bUrMSeP0H/2+uykpILfITXJvQz
EQsim98z0YtN3ZjTg6zL1p2NsHf8lyqljS6MpJ+5iN6zptJenpI4V1LhhR8Dzf5M
Ei9r97Wcoyk=
MIIDPzCCAicCFC9Nqrnxbcu0Bfr7MfkIFOdpiRJuMA0GCSqGSIb3DQEBCwUAMFox
CzAJBgNVBAYTAkZSMQwwCgYDVQQIDANJZEYxDjAMBgNVBAcMBVBhcmlzMREwDwYD
VQQKDAhBY21lVGVzdDEaMBgGA1UEAwwRQWNtZSBUZXN0IFJvb3QgQ0EwHhcNMjMx
MjA2MTMyODUyWhcNMzMxMjAzMTMyODUyWjBeMQswCQYDVQQGEwJGUjEMMAoGA1UE
CAwDSWRGMQ4wDAYDVQQHDAVQYXJpczERMA8GA1UECgwIQWNtZVRlc3QxHjAcBgNV
BAMMFW93bmVyLmNsaWVudEBhY21lLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAPR/B6l2zRUk9PrT2Td6hBVF1RhJIDOdlhWD+H++qZgkfdUlajkV
/1VxPmAcTLQaWVWgDEtWKwe7vtnmf5gFLeE+5HczS7+d+15YHBfKe1d9RL73mC+j
17GZ6Zi3uQl5FkKXzIPeFwv9SA+d2IXs+wVaweGst48MNnLnZW0JX+B71Jw0yGry
wvORJcCqBMOKUBp0ILRbUJLOWBs5RVcHWuvJqILARHaDxMVmmmVO48zvBtNaUT0Z
o9znF0Rfc/BcfpJmrsXaZNj6bDk44iqL5BIt/3u7r/tVHVPmTQ4ETw8OMTlJejtU
IjPmAGayXexACAWsy18WCmvpRIVM7Xr3Sb0CAwEAATANBgkqhkiG9w0BAQsFAAOC
AQEAFLKbgkPVKD79lWf1/S56BJ0TgwX+YR5ASZfdpSNt2TOheAF1aezOAmF+lCsV
55lJbrNPUZmTQxM2mBTB7yeIk/pGI5hkXJZ/35VyOVswlCSW1dfXDh4p5L6W0sek
bx0UlLEKXw3ZI+8RqonshLG6ANu69SWLllOLmMGHdUVKPERJwQ9TdI72kDi5rU2w
ERZxcgAcTO7qFwhlvqyZSn+hvvGny+0bZeUnrJM273ob83fyRWVxot30TjnvNsgp
P/Hgkube8MT9i4rYS/xjZV1QP7AjQUESAS849J+KQ34+CJ91ig042imBtm4aMuGs
ZuuIvtVDySUk7QSviiVo3DZMXw==
-----END CERTIFICATE-----

View file

@ -1,17 +1,17 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICozCCAYsCAQAwXjELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lkRjEOMAwGA1UE
BwwFUGFyaXMxETAPBgNVBAoMCEFjbWVUZXN0MR4wHAYDVQQDDBVvd25lci5jbGll
bnRAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUx6zA
fpMuhi1mBkD1WJdLMEltK19rE8av/PZHZMkt57W9vL1V80HU1OLoc28t8UTjsGGf
lZ9XDDZltpQ6Z63cjEuDMAtILzqHCzLCAjziu/9M9KCpLtuRJDI3aw5Awm882szH
8yTQ2M+sxxGpnDUgKUqDSAYTfZDtUbl+9sr/uXxPVn2gYxvumOeYyKEla4IxvI8Q
LQk+QozB7uY16CeVmB33v1dl+fnjU9wq4Q0aHaD3oVr0spfrQgrkrWdz7O7UGmrh
MROmm0IEBWQWIYCBTM2BiuYkzxrv6ywJqhzpCpsordFB49YVd5JleuYsObRH4QUM
d4Vr2iJamUnsChjdAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAUBbz0xfbqAIX
pi5ngW7kHLszgkTpoPPaq828iGCHvyNsmkZ8ZYm9dnK82nhYoxk6XlmrfIfVzalt
WyK2k1tui5WhEarMn6xDSLHV1RBlLR1a3vnrmYkDEF1hlOmEu12X3d1RVUeS39DX
nWB/oXq1CD39g0jV8WkOnrqHcr75JVTecO9qloFjHaEP52wXSyM2Ty6DDJhmihPA
nVmp6S70/WOWLlOUgxV0lBac4N4Qi6dac2MRxZZsSmISzA9TzKW/Pl/eYXXM5k2u
BRISXoQQWK0UOBeQ5vITdfJEnqbC5l90VqF8aKLKInfYtyJKVYxBdwykw5xQGJWK
5ZuwVtbE0g==
bnRAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD0fwep
ds0VJPT609k3eoQVRdUYSSAznZYVg/h/vqmYJH3VJWo5Ff9VcT5gHEy0GllVoAxL
VisHu77Z5n+YBS3hPuR3M0u/nfteWBwXyntXfUS+95gvo9exmemYt7kJeRZCl8yD
3hcL/UgPndiF7PsFWsHhrLePDDZy52VtCV/ge9ScNMhq8sLzkSXAqgTDilAadCC0
W1CSzlgbOUVXB1rryaiCwER2g8TFZpplTuPM7wbTWlE9GaPc5xdEX3PwXH6SZq7F
2mTY+mw5OOIqi+QSLf97u6/7VR1T5k0OBE8PDjE5SXo7VCIz5gBmsl3sQAgFrMtf
Fgpr6USFTO1690m9AgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAVHsjfGbzlq9w
J6eAmgZYLdYH/fip2okie7Wxcs73VvJ4jI/ccCnQwuzybPa6oBeWfHoPpnOjDh1c
qn5bTS29VXOo1OIf+TknJdN5VvcNrQsKY5Z7tlaLG8KYoPF5i83J5JuAtaDtl1p6
yflW+RFRyPYtfeR0EgL0H1meeri710z/ZzLwoEuasbjngen6KxNdfJ3ET9UmgOa1
ItG0WYzu5zcs77kH3GVSenJcKXccz9nel7S9XDaXnMs8Y3HB49MnMral2L0WVzpL
ydhfUNIVb2ZQ7dj1/t0s4Bl7em0ZW/VRJTeMfKRVN/lC2uIsUH3DgOarpkDSEXup
QJla4TtSvQ==
-----END CERTIFICATE REQUEST-----

View file

@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCUx6zAfpMuhi1m
BkD1WJdLMEltK19rE8av/PZHZMkt57W9vL1V80HU1OLoc28t8UTjsGGflZ9XDDZl
tpQ6Z63cjEuDMAtILzqHCzLCAjziu/9M9KCpLtuRJDI3aw5Awm882szH8yTQ2M+s
xxGpnDUgKUqDSAYTfZDtUbl+9sr/uXxPVn2gYxvumOeYyKEla4IxvI8QLQk+QozB
7uY16CeVmB33v1dl+fnjU9wq4Q0aHaD3oVr0spfrQgrkrWdz7O7UGmrhMROmm0IE
BWQWIYCBTM2BiuYkzxrv6ywJqhzpCpsordFB49YVd5JleuYsObRH4QUMd4Vr2iJa
mUnsChjdAgMBAAECggEBAI7PzCdKWIVEDrfsMNZRH4jw5MqB46mo5gNwUgbd267Q
yEdEsImcYwilANoYVBRJj9LvMAY7XSP1eRHRXB9j/iPCF4npyCqWCxwtx58q/r61
rq5z8vPzIpgDoqBHhvrqqFRFwmP8JM7EDzTOMUoZw3UHrZquvAZhdYgX9nLE+r7U
Gqybj1kLlTkTTPkNittCA4su6r4JXbH22ydQ7EdEqS+4mIzodKxW6cuxCscah4kb
3D5xkX9eXp0zYvmPTXLiIXTzcDLTlKHoDUM5VFgKcO9677Z5vqIWHudAY8ip93GW
EM6gU8wTURvw05V/A0+pD+Cksen42pyz7Myeh2SEjBECgYEAxL/OpyYyhnwIoIla
/SJh7YdJQnUt46nglrcRaKOmNHSbFsB7oC/Dy/GEDKhobupW7BP275DsG183NiWH
HxW1qPLAmF+Zo4AbwGXoMyUoAfGOY4qYuxNMSqe4UfklZQM7hpxYMl4ww9pGSrPf
l2ZTrTypjf/PDWFfk8xYCDDzx58CgYEAwZW5LQHefGS1h2vHJv7d5WoxMABoEv5A
Nnon4aVle2/lRyHnbkSaU+THLF+hqN3FevKNbdgID7trLaKz/AQ7+dFxoccZMK1r
5SBbomQ+AN+N2CzqweHhFxnThFuiERGnrauIxZBP71neP85PXmeyn9HaAQ66+W0k
4YmR1N6B/gMCgYEAgTOoJHHcCh6Xl7y7iLG0d0Q5IrBKs0+SUcbkYr/c3GOamOVM
CdR0EpCGc71/3x56eEtgBNA5jHwJiMA6LHqYMdtACI/3F7x//OevBS8oR6Z2J/4S
7/7rQbdEPmiLWvs1ct9mt6TNB0BmmpXmhcjHuGK2wa40LQg9uJQfVzm2TOcCgYEA
mwOIr8iiXxvJ3PHqHIjUKQP69UQuEp9zzevNibwG4mw8vdQhXUQkDG/TKe4+JlnH
6+bUZ5QpCGf8sCNsWq8NU8NqabBjtH36OxTJK+V3XP2muOdm6PA2bWgp3v/9bAum
KMSGFiW0TKa6+H79QUxqVd4V4ujnwhmrActGcp3mm/8CgYEApw7jpRIG31ZCVAcr
BmeAECVFG8JzV6qrw8DEW44TY/6DcCKRhsFmRYJxMfw46EFYXIAOuF0CaqI7t29A
K4HNlFDg0j6rU2mb2F1W+sikfd3Z2Jbmayq6zRDioqFK9K7B8K+NsJSdb/TChtHk
EMpZ22XlST/rMKktdpoJlbmHgRE=
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD0fwepds0VJPT6
09k3eoQVRdUYSSAznZYVg/h/vqmYJH3VJWo5Ff9VcT5gHEy0GllVoAxLVisHu77Z
5n+YBS3hPuR3M0u/nfteWBwXyntXfUS+95gvo9exmemYt7kJeRZCl8yD3hcL/UgP
ndiF7PsFWsHhrLePDDZy52VtCV/ge9ScNMhq8sLzkSXAqgTDilAadCC0W1CSzlgb
OUVXB1rryaiCwER2g8TFZpplTuPM7wbTWlE9GaPc5xdEX3PwXH6SZq7F2mTY+mw5
OOIqi+QSLf97u6/7VR1T5k0OBE8PDjE5SXo7VCIz5gBmsl3sQAgFrMtfFgpr6USF
TO1690m9AgMBAAECggEABrt3DfZGEY07Nqs92bPPWRjwvsgIBuEnDbrHeAMGFKOl
QwjSSuCdb4WcVVVU0l0VtSXLTjQWSr1OH8i484oUTZW9AOrswh8QRHSHeis6krnn
D51t4hqa7kUXRCaDm0HA4xQJebRpl4xW9IvLjMKDVfjTX7Vbt5Nl7hY2Oiv408uf
U0qhKAXQ/Ntrw1jGm5gc3V7SdNokXxSlIu6e9gcozi62qG0dggcEvE8JlrrU46EM
m9bOUkdU1Zp4LYgKNlVmly+do0ksl3ia0POQUWJh11UosIhPmuJ1Tvu/eBcdO4y3
/5iKQGmB7/FCXtl94QXt3LsiyIO5yDwq4Mwfihd4QQKBgQD2Xz9s/ZqFTUFjZiIq
7kiPHnM3MMsdaRihCWShtMOfuO9itDvVPxMsFy+bKgaL1jg27/rp+2ee/8/btMtc
gGBTwzoTJ1nDl3yMPH8Dqrz57yBxroXTvqzgw/DqUtt8dpZwS6nSj3R59LNtIYOw
a1cze8WQtYI5sJ75M7BshVeqwQKBgQD+DQQMEdwriaso5Jpq4XYJE2hjjHyOTPjF
0vYlNu388zOe1xKu3kbE6xr7bzD9l47JDCJkZUTdVEIYCU/GSYlXbJA7KumTvyEI
kDUkw2Z5ER6M6VWSBBG975+7ZL4S3u6scZuoPuA15vSt8sTRJ+UxjZqNHxWWgNO0
vQMCeMLJ/QKBgFQ79oyGHLms00lv1wjElYxRReU9yoRQtXoBoyVij2vhp/8gTkC3
ow91EfLEKJ7Q7EFkRW6QZTpIjkG5C7qhrc2VtxprXtZvycaMvIR9cMUnS/lySRE/
ZQ4oEAudrqtcRE06oWJ7Z8B2uI38YnEEEjlo/QBf/plI2OGtGYnRAUtBAoGBAJQY
ik9u6eYHbtt7x/ieTeykf6nob4OUo0DO5eZzyvA15dWmkqGVubN5SlVgUDLEctwI
UtQrpKVRROY2J2bz3zgzdTDIpzkVpfAUAKbZsZbJwAFs1BE2hhEu6vKWm6bshj3I
THEbliBV7jSo3MsAcr8cSQgTIsaFeuRVereqJgMRAoGBANZOGAmDceSNJLUxwoMD
0wMhc178ghhGAWeN4z+5OqHDCWEnyTC2WIeUQZ9muJCfv6rOTw/wKEP7J5IRECin
RJeemh6mw4jTMPJSmfq3tjuy1gFXCzmSlI78ekSfvV8vJDLJJ6h+QFQoXXYAPDOC
Y7Mi8PnYxe/pvvhSjL1aI/aD
-----END PRIVATE KEY-----

View file

@ -0,0 +1,5 @@
#! /bin/bash
rm output.p12
openssl pkcs12 -export -inkey cert.key -CAfile subca.pem -passout pass:secret -out output.p12 -in cert.pem -certfile subca.pem

View file

@ -1,20 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDMzCCAhsCCQDTJGaQvJLD0jANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJG
UjEMMAoGA1UECAwDSWRGMQ4wDAYDVQQHDAVQYXJpczERMA8GA1UECgwIQWNtZVRl
c3QxGjAYBgNVBAMMEUFjbWUgVGVzdCBSb290IENBMB4XDTIzMDUxOTE1Mjk0NloX
DTMzMDUxNjE1Mjk0NlowXTELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lkRjEOMAwG
A1UEBwwFUGFyaXMxETAPBgNVBAoMCEFjbWVUZXN0MR0wGwYDVQQDDBR1c2VyLmNs
aWVudEBhY21lLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALM0
r97hLAuvLGxZtvTRR5BTC57SiRXMezsMMnsjatq3o6KWhXw9zBDb6FF2be9ftyO6
Mbyo1RGAmHw6o+Sei9p0jImOWIGitibhhYj/8PtyDoHurlKlf5i5/qZ+hRuFVrqv
76E9V+fSE22whOgTbHY0L5yYhWGAGn9MUhod7CjE4Q5HHrZCNIu69Dhgv+eB+Xlc
arco0sdy975Pv4OujQGBwJp3J5RyvnvMIVf+UKnuA3eK9C9nv4TCocOhluY7oFM/
VrmFaMbHDskFM0miJKUvj8Rg2PN8YuA4VtyYUkQFYBBwiGbgX4AZ7gnAAbVVjvbr
DIqnFLNRnSvEaKPxhV0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAbrnCV9FmZfeS
MiCiOkp4XOzDYZ2ENr8kN7+UNul9ngREXM3aCvK+DsKlvdqySoyFiwX9hkIOK6AL
QvPW5veI4kwi1Bh5UnzwPCSrEvrMF41bne9M/GQ2X0V2mZtCfagP3BFcqFliSvVB
qXdiujnf1tREYLHFHmefPPhtO21mnh7LJsEtkccFKcbrMAWzrKGuVR5T9XNpfPUZ
zB6i24369TKm2Mrmw7yuVTBtdyZDaigckLvDuI61suM+Yd7MtjJE471A2Csjg1MI
Zk8AW9ntN/EF5rsbgo2IzxEpfGxZH7vW5AiuLiuJcLsFs4L+WmntVL9FoQ0df80y
MZRp6z78Mw==
MIIDPjCCAiYCFGna/+9McMd1iqu/lOb1Tr6GhDEcMA0GCSqGSIb3DQEBCwUAMFox
CzAJBgNVBAYTAkZSMQwwCgYDVQQIDANJZEYxDjAMBgNVBAcMBVBhcmlzMREwDwYD
VQQKDAhBY21lVGVzdDEaMBgGA1UEAwwRQWNtZSBUZXN0IFJvb3QgQ0EwHhcNMjMx
MjA2MTMyODUyWhcNMzMxMjAzMTMyODUyWjBdMQswCQYDVQQGEwJGUjEMMAoGA1UE
CAwDSWRGMQ4wDAYDVQQHDAVQYXJpczERMA8GA1UECgwIQWNtZVRlc3QxHTAbBgNV
BAMMFHVzZXIuY2xpZW50QGFjbWUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAoNrvWrA14HfujCmcvrlDeNnUfKZn/aOpzozUOeTtSKBjmYwMPhSU
z6Ykt6DqDiw81vpJpal3GmWkbrHtb1FAjvtBosyxAnluAd8ErcacMkWfGXF0s9wl
06bEQKBzBOqmIZ3w1yGWEno+QGISojb+jtPJ+LUgJTwTaGMfcpmgTsnOAHd5pOzD
60py/xZr5hJRnO782uraFZ2l7WCiSwd2nmjMZEgAuLvqffjHPCuowcLdSlulgwgN
8LGY2B8JUW0xAgIaK3MVW0B5pm1iWr7135gtx+oU9sZ/xsirFDAlhIunhmJ7Gzrh
tD9/UX0QpkDRvm7OngC6DIem5JKKSRGCSwIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
AQBuPFfCSx3eMFjd8IExo5Yz1liVueV/Svl8TWkTr5J1hXMsY7xeg4BfRTz6+Sft
UQkSfMASy/GTWY6i7g8i4sc+FJd3ph4o4OaRNA2YzEk5sUVsTxwL+0xIb5TtJZaD
GyHu4JvZ/lrVAbFmLLJMiHXVA6bYAZADg43Bao3WrKhK6BE5gMQ0ZzwnIn6yqWju
abvDe3HX6RsWqxczIf8dsO1TEAnwbcq4034ATpXLzgdzrX3zSWj50kgwcqB2c8jN
yK1u8I5Z8YNkfgpeekZNCsqrKzWGqtwyxUpZk11gO6LP1UCgt6E0hpBXxXCDbQRs
LXhVsjWXWOBJIHTafzp13tpC
-----END CERTIFICATE-----

View file

@ -1,17 +1,17 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICojCCAYoCAQAwXTELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lkRjEOMAwGA1UE
BwwFUGFyaXMxETAPBgNVBAoMCEFjbWVUZXN0MR0wGwYDVQQDDBR1c2VyLmNsaWVu
dEBhY21lLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALM0r97h
LAuvLGxZtvTRR5BTC57SiRXMezsMMnsjatq3o6KWhXw9zBDb6FF2be9ftyO6Mbyo
1RGAmHw6o+Sei9p0jImOWIGitibhhYj/8PtyDoHurlKlf5i5/qZ+hRuFVrqv76E9
V+fSE22whOgTbHY0L5yYhWGAGn9MUhod7CjE4Q5HHrZCNIu69Dhgv+eB+Xlcarco
0sdy975Pv4OujQGBwJp3J5RyvnvMIVf+UKnuA3eK9C9nv4TCocOhluY7oFM/VrmF
aMbHDskFM0miJKUvj8Rg2PN8YuA4VtyYUkQFYBBwiGbgX4AZ7gnAAbVVjvbrDIqn
FLNRnSvEaKPxhV0CAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQBj8GXhASmFJ0z8
S5TR45z/J7omLW8sEsD1XfSHBRpPzZa8+K8tpIVCztP8zuQZqmUv+yf6K9miLyjW
OqJgkSOLoj3sZIOfEeKqGzviQosdS2xxhE68U6/Wy8S6BXlfo5rAYsQPAT5tpZJw
2OBT7sl4mfYqyG7PN7zM0HuMC18epw3ndrl6KOOS2kg/RPJks4eRf1kOnZHXHAsn
g0HoVHEaYRlKVQXhWQmO0mgeJgtNRSPFCs1MXP8kefGvgJCj77WBHewKq3INA9Ck
ryVQ5eesI6ByhcXHLN3ZdiBC4dESxEjHWLIJ+XDUa56cZ9OvWjQxybEYYiYYEYOI
hf91pd57
dEBhY21lLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDa71qw
NeB37owpnL65Q3jZ1HymZ/2jqc6M1Dnk7UigY5mMDD4UlM+mJLeg6g4sPNb6SaWp
dxplpG6x7W9RQI77QaLMsQJ5bgHfBK3GnDJFnxlxdLPcJdOmxECgcwTqpiGd8Nch
lhJ6PkBiEqI2/o7Tyfi1ICU8E2hjH3KZoE7JzgB3eaTsw+tKcv8Wa+YSUZzu/Nrq
2hWdpe1goksHdp5ozGRIALi76n34xzwrqMHC3UpbpYMIDfCxmNgfCVFtMQICGitz
FVtAeaZtYlq+9d+YLcfqFPbGf8bIqxQwJYSLp4Ziexs64bQ/f1F9EKZA0b5uzp4A
ugyHpuSSikkRgksCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQAz3Mej3vhOAFPU
Jc5H+cYNdPLPahjJDwCzrlhSg8FcgNE9NoP2WZqln1PcYHGVSCr2P19w7pfK9/9r
HxbKunNkafG7JYtUtVHqvK1jtoVA3AD7nPMpeqIYudKIICUPnknarYOZcvkf5Iit
uRGwKUsPHMR1HVZA1fes38zzAwJWJdnR6X4ws10Q1RLKhDu2ighGPpzIAtOTPSLf
uQcrzri/bZw5Hur4pisA1KKGClI9nuSh18dvsW1t15qFCVfCXpSS2Zwy5AOcS+C4
Q30ZXftVn4rz0hOAv2Gn3xp5baUGO13JQVq/WUbr+M4MHiW7hSdrSsCWhyBLVKd1
9wryWIFq
-----END CERTIFICATE REQUEST-----

View file

@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCzNK/e4SwLryxs
Wbb00UeQUwue0okVzHs7DDJ7I2rat6OiloV8PcwQ2+hRdm3vX7cjujG8qNURgJh8
OqPknovadIyJjliBorYm4YWI//D7cg6B7q5SpX+Yuf6mfoUbhVa6r++hPVfn0hNt
sIToE2x2NC+cmIVhgBp/TFIaHewoxOEORx62QjSLuvQ4YL/ngfl5XGq3KNLHcve+
T7+Dro0BgcCadyeUcr57zCFX/lCp7gN3ivQvZ7+EwqHDoZbmO6BTP1a5hWjGxw7J
BTNJoiSlL4/EYNjzfGLgOFbcmFJEBWAQcIhm4F+AGe4JwAG1VY726wyKpxSzUZ0r
xGij8YVdAgMBAAECggEAcncz4xRG8wuoP2kqsMPAh1JUONVX1MGw8O2AjSD5Ts0/
y0mD5/D9GMOuZ+JSpDSqITr8K/pXZn+SZr96EjynLhmF7xMqO1u4J6ujhPeecK3Q
0HOpi4bEssQTATwNP5/y3vokAQTS0nNgD49RxX8qp3H7+3V8tOODiU4xnxb/0qWk
wzd/CFdED+7UH6PZvx0O5gNpr6vFaQ1WX9mv+DKvlPDW44O2zNsOgoc1tsj0dBPp
Ut8AlhLBohnddRpam0nqDZYB5ipv7KV4Qe7C6nfMaZOx13jQ6Zh6r7gONJZgXWUv
RT6iCCATBXjItOtupkqd/rxeiu+E3n37JRmzck80AQKBgQDkLHvbJ19gJZK2+dHq
tFjb3k2yagHz3EEtZc81f/vs+5srJd8LfFKjeb1BhQkI8s6JXXIdtJH8Cj3Tb7xT
AFG2KCxHIkjbw6/b8F5AOr3Jh0i81pwMZqsvK7T7/QrLqpCiDXMSYEXGodc9KQ8j
jSR9IdVdyYLFrgq69+/1ueJ/AQKBgQDJD3A3E9Qelz8tlGtd5jqmDcUMcZdXC89U
IFMwFkjofmXDJ3yOvmTVyhv8AqR8sxpU1ea+PmTGnE9SnJ85+AAIRY3ZwVI5OTEc
SKnB42mn3R6n20DP/ATFplhZ9UcOs2aP/wcgvzeflGSR0BlHrNWyUZTT7CbPpCn9
fT2y8wtiXQKBgCc7DsCLcHaTfzo+kb0M7QpAmpNGbreC8yHfE1Vbf/ErAW7VwBe1
dG/vCn8tZHRJawFrM6ev2DnBeQfVeke7ElGDbrl8R5KhfqcOGOZqMx9nRzZQn9vx
+xTNKvz4cNa7qMp0DYjnTJfuU5eMa7HtHoz1OD4722QpnGaxImzZXPwBAoGAd+ok
REUoOwJ75MRjcMJycj0+K9jHqVva0NLiqohv/XH9YXJ4gG4fl76OCDTPQ6xSZQya
LRbrZeUuDhhpgtpdspFBNAFv978bgkH4NTJb/okL0pMaybEGa5d1lFmMgsEOx40U
BDzngN6xSKHzoeL8JPkxKV/sGeVQQeZjWQpJ3OUCgYAQX1begoozsV7UCabhON8I
EX0457fgS64eR8TZIHazEsXQ+sJOIBEWPeFqNlbeUtrdVqvMFw6IBFJ5I9K0P9X1
pveKBTkP4NRQvEyAd49hzsKBhkMLFTTbDN9fxA9VU4wP12ARPv5V1NdeHbYQOBSC
woxbGNZ9VRIGlsTp0Mty5g==
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCg2u9asDXgd+6M
KZy+uUN42dR8pmf9o6nOjNQ55O1IoGOZjAw+FJTPpiS3oOoOLDzW+kmlqXcaZaRu
se1vUUCO+0GizLECeW4B3wStxpwyRZ8ZcXSz3CXTpsRAoHME6qYhnfDXIZYSej5A
YhKiNv6O08n4tSAlPBNoYx9ymaBOyc4Ad3mk7MPrSnL/FmvmElGc7vza6toVnaXt
YKJLB3aeaMxkSAC4u+p9+Mc8K6jBwt1KW6WDCA3wsZjYHwlRbTECAhorcxVbQHmm
bWJavvXfmC3H6hT2xn/GyKsUMCWEi6eGYnsbOuG0P39RfRCmQNG+bs6eALoMh6bk
kopJEYJLAgMBAAECggEAAlgOYtjsgA/fVwWINjNG62zpLsPSAh0vbUYaFZnjZ0qH
CQbEaucr3/bA1DgxEPHOFSfiiNp2VNkkU0SefB/H/A1nQHMRJUMVuktAqzOYtRbU
oa9IjxJnLrVgaLyt0GWG9Dhz+P6RTEWhwJhtQxXgEb6+U7R3+nLsqXnATJA2iM9c
FmGuBJnUe6ej5DZVCk1TOmoOa12MUpRYmO45A8IdhbTKfj+3IVXhKvIVFMwfZONj
T/uPmplE7ElR/YlVBGeiKhQuuhWNnf0qwfooaAu1tbZU9jXUzDuK4ITZG7HD14ov
qn494MC234ETu8yzhlhqmpF5iu6f335ev815Hu8EgQKBgQDZqtxY5Ktys5GkDD+Z
6HvES1DpipBODlRZrr4jOvCJGvfmsW1LYQUMbt8VfYe3xJ6juC+6p2+XeyACNbow
W8My4/jhYwyzOOKaIPKhHHxj6VX/+6/q/mkuF0E9OGyRzULsRmfcs4R42XhkiK5d
OELWxRaN1OB0viPjg3N6t1ATwQKBgQC9LsycyfI3Wol7nuAigenHBa3vjGiR5EJH
4apI61SBzM5l0m4Kkq9+rOwdzmbFj0tO6qqNzBT4214L0PNO+lCTPMUvmOCzNkLd
39bdmnFHU80fcfINidTYgTpTtgBIk5/L9K2V1B6ErK8u7BM87vIzANRWHyEgjMh9
XmGPmCbpCwKBgQCmDETZ/1+XOctR1RIzy/mWzknkTxM0qJPKcpaT86OJRCx3LUUO
Ku4aBP5ju4ZsNy86AAlRH+fuLDxH00q9F1N6oBS9g70tf6jGIXpWU4/E/Dzh8dPK
tdfxFEZteHzCYU67Rh53a+8T7fp83xP9Ay1h/6nr+shb5q0kI6RVqkAJQQKBgQCb
mXVjc9oL2yR/D+KLn0y3jB1OjK6paBN36czUIvKju9768oPdoF5Fk1a1AUGYNk6b
fvl+nR+Wu17z2w1zpQBGydHpXxVZcUS4FtYz+EY5g1yQA3kx127AEDH4DhbXmsvw
xbNMK2Zae2mu7y63jFSazJDYsMvfaMuyCcG2iE9glwKBgCLw6YsWiZngpESy57e/
upHXQMd2Hc9k7f2fR1cSuZ5m0NS9dbMxCdX53c22DhPj261Ej8PbIKM8gUAs3X/U
ABZ63xJfGC0vGaO8PKHnDYSa0Br8Guq+G3K/1fFv8ujkX8gcBnt3w3A3iIFsw8GC
9XWiwNeQej22P7nnLl5GjHqz
-----END PRIVATE KEY-----

View file

@ -11,16 +11,11 @@ cosmian_kmip = { path = "../kmip" }
cosmian_kms_utils = { path = "../utils" }
http = { workspace = true }
josekit = { workspace = true }
openssl = { workspace = true }
ratls = { workspace = true }
log = "0.4"
reqwest = { workspace = true }
rustls = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tee_attestation = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
tokio-util = { workspace = true }
url = { workspace = true }
webpki-roots = { workspace = true }
log = { version = "0.4.20", features = [] }

View file

@ -1,335 +0,0 @@
use std::{
io::{BufReader, Read},
path::PathBuf,
time::Duration,
};
// re-export the kmip module as kmip
use cosmian_kms_utils::access::SuccessResponse;
use http::{HeaderMap, HeaderValue, StatusCode};
use openssl::x509::X509;
use ratls::verify::{get_server_certificate, verify_ratls};
use reqwest::{
multipart::{Form, Part},
Body, Certificate, Client, ClientBuilder, Identity, Response,
};
use serde::{Deserialize, Serialize};
use tee_attestation::TeeMeasurement;
use tokio_util::codec::{BytesCodec, FramedRead};
use url::Url;
use crate::error::RestClientError;
/// A struct implementing some of the 50+ operations a KMIP client should implement:
/// <https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=kmip>
#[derive(Clone)]
pub struct BootstrapRestClient {
server_url: String,
client: Client,
}
impl BootstrapRestClient {
/// Upload a PKCS12 file containing the KMS server's SSL certificate and private key.
/// The KMS server will be started in HTTPS mode.
///
/// Call the `pkcs12_password()` method to supply the PKCS12 password
/// if it is a non-empty string,
pub async fn upload_pkcs12(
&self,
pkcs12_file: &PathBuf,
) -> Result<SuccessResponse, RestClientError> {
self.upload("/pkcs12", pkcs12_file).await
}
pub async fn set_pkcs12_password(
&self,
pkcs12_password: &str,
) -> Result<SuccessResponse, RestClientError> {
#[derive(Serialize)]
pub struct PasswordConfig {
pub password: String,
}
self.post(
"/pkcs12-password",
Some(&PasswordConfig {
password: pkcs12_password.to_string(),
}),
)
.await
}
pub async fn set_redis_findex_config(
&self,
database_url: &str,
master_password: &str,
findex_label: &str,
) -> Result<SuccessResponse, RestClientError> {
#[derive(Serialize)]
pub struct RedisFindexConfig {
pub url: String,
pub master_password: String,
pub findex_label: String,
}
self.post(
"/redis-findex",
Some(&RedisFindexConfig {
url: database_url.to_string(),
master_password: master_password.to_string(),
findex_label: findex_label.to_string(),
}),
)
.await
}
pub async fn set_postgresql_config(
&self,
database_url: &str,
) -> Result<SuccessResponse, RestClientError> {
#[derive(Serialize)]
pub struct UrlConfig {
pub url: String,
}
self.post(
"/postgresql",
Some(&UrlConfig {
url: database_url.to_string(),
}),
)
.await
}
pub async fn set_mysql_config(
&self,
database_url: &str,
) -> Result<SuccessResponse, RestClientError> {
#[derive(Serialize)]
pub struct UrlConfig {
pub url: String,
}
self.post(
"/mysql",
Some(&UrlConfig {
url: database_url.to_string(),
}),
)
.await
}
pub async fn set_sqlite_config(
&self,
path: &PathBuf,
) -> Result<SuccessResponse, RestClientError> {
#[derive(Serialize)]
pub struct PathConfig {
pub path: String,
}
self.post(
"/sqlite",
Some(&PathConfig {
path: path
.to_str()
.ok_or_else(|| {
RestClientError::Default(format!("Invalid sqlite path: {path:?}"))
})?
.to_string(),
}),
)
.await
}
pub async fn set_sqlite_enc_config(
&self,
path: &PathBuf,
) -> Result<SuccessResponse, RestClientError> {
#[derive(Serialize)]
pub struct PathConfig {
pub path: String,
}
self.post(
"/sqlite-enc",
Some(&PathConfig {
path: path
.to_str()
.ok_or_else(|| {
RestClientError::Default(format!("Invalid sqlite-enc path: {path:?}"))
})?
.to_string(),
}),
)
.await
}
pub async fn start_kms_server(
&self,
clear_database: bool,
) -> Result<SuccessResponse, RestClientError> {
#[derive(Serialize)]
pub struct StartKmsServer {
pub clear_database: bool,
}
self.post("/start", Some(&StartKmsServer { clear_database }))
.await
}
}
impl BootstrapRestClient {
/// Instantiate a new KMIP REST Client
#[allow(dead_code)]
pub fn instantiate(
bootstrap_server_url: &str,
bearer_token: Option<&str>,
ssl_client_pkcs12_path: Option<&str>,
ssl_client_pkcs12_password: Option<&str>,
measurement: TeeMeasurement,
) -> Result<Self, RestClientError> {
let server_url = match bootstrap_server_url.strip_suffix('/') {
Some(s) => s.to_string(),
None => bootstrap_server_url.to_string(),
};
let mut headers = HeaderMap::new();
if let Some(bearer_token) = bearer_token {
headers.insert(
"Authorization",
HeaderValue::from_str(format!("Bearer {bearer_token}").as_str())?,
);
}
headers.insert("Connection", HeaderValue::from_static("keep-alive"));
let builder = ClientBuilder::new();
// If a PKCS12 file is provided, use it to build the client
let builder = match ssl_client_pkcs12_path {
Some(ssl_client_pkcs12) => {
let mut pkcs12 = BufReader::new(std::fs::File::open(ssl_client_pkcs12)?);
let mut pkcs12_bytes = vec![];
pkcs12.read_to_end(&mut pkcs12_bytes)?;
let pkcs12 = Identity::from_pkcs12_der(
&pkcs12_bytes,
ssl_client_pkcs12_password.unwrap_or(""),
)?;
builder.identity(pkcs12)
}
None => builder,
};
// Get and verify the ratls certificate in order to use it as the only valid root CA
let bootstrap_server_url = Url::parse(bootstrap_server_url)?;
let ratls_cert = get_server_certificate(
bootstrap_server_url
.host_str()
.ok_or(RestClientError::Default(
"Missing 'hostname' in bootstrap server url".to_string(),
))?,
u32::from(bootstrap_server_url.port().unwrap_or(443)),
)
.map_err(|e| RestClientError::RatlsError(format!("Can't get RATLS certificate: {e}")))?;
let ratls_cert = X509::from_der(&ratls_cert)
.map_err(|e| {
RestClientError::RatlsError(format!("Can't convert certificate to DER: {e}"))
})?
.to_pem()
.map_err(|e| {
RestClientError::RatlsError(format!("Can't convert certificate to PEM: {e}"))
})?;
verify_ratls(&ratls_cert, measurement)
.map_err(|e| RestClientError::RatlsError(e.to_string()))?;
let ratls_cert = Certificate::from_pem(&ratls_cert)?;
// Build the client
Ok(Self {
client: builder
.danger_accept_invalid_certs(true)
.tls_built_in_root_certs(false) // Disallow all root certs from the system
.add_root_certificate(ratls_cert) // Allow our ratls cert
.connect_timeout(Duration::from_secs(5))
.tcp_keepalive(Duration::from_secs(30))
.default_headers(headers)
.build()?,
server_url,
})
}
pub async fn post<O, R>(&self, endpoint: &str, data: Option<&O>) -> Result<R, RestClientError>
where
O: Serialize,
R: serde::de::DeserializeOwned + Sized + 'static,
{
let server_url = format!("{}{endpoint}", self.server_url);
let response = match data {
Some(d) => self.client.post(server_url).json(d).send().await?,
None => self.client.post(server_url).send().await?,
};
let status_code = response.status();
if status_code.is_success() {
return Ok(response.json::<R>().await?)
}
// process error
let p = handle_error(response).await?;
Err(RestClientError::RequestFailed(p))
}
pub async fn upload<R>(&self, endpoint: &str, file: &PathBuf) -> Result<R, RestClientError>
where
R: serde::de::DeserializeOwned + Sized + 'static,
{
let server_url = format!("{}{endpoint}", self.server_url);
// open the file async
let file = tokio::fs::File::open(file).await?;
// create a body wrapping the async file stream
let stream = FramedRead::new(file, BytesCodec::new());
let file_body = Body::wrap_stream(stream);
//make a form part of the file
let file_part = Part::stream(file_body)
.file_name("bootstrap.p12")
.mime_str("application/octet-stream")?;
//create the multipart form
let form = Form::new().part("file", file_part);
//send request
let response = self.client.post(server_url).multipart(form).send().await?;
// check the status code response
let status_code = response.status();
if status_code.is_success() {
return Ok(response.json::<R>().await?)
}
// process error
let p = handle_error(response).await?;
Err(RestClientError::RequestFailed(p))
}
}
#[derive(Deserialize, Serialize, Debug)]
pub struct ErrorPayload {
pub error: String,
pub messages: Option<Vec<String>>,
}
/// Some errors are returned by the Middleware without going through our own error manager.
/// In that case, we make the error clearer here for the client.
async fn handle_error(response: Response) -> Result<String, RestClientError> {
let status = response.status();
let text = response.text().await?;
if !text.is_empty() {
Ok(text)
} else {
Ok(match status {
StatusCode::NOT_FOUND => "Bootstrap server endpoint does not exist".to_string(),
StatusCode::UNAUTHORIZED => "Bad authorization token".to_string(),
_ => format!("{status} {text}"),
})
}
}

View file

@ -5,7 +5,6 @@ use std::{
time::Duration,
};
use base64::{engine::general_purpose::STANDARD as b64, Engine as _};
// re-export the kmip module as kmip
use cosmian_kmip::kmip::{
kmip_operations::{
@ -17,12 +16,8 @@ use cosmian_kmip::kmip::{
},
ttlv::{deserializer::from_ttlv, serializer::to_ttlv, TTLV},
};
use cosmian_kms_utils::{
access::{
Access, AccessRightsObtainedResponse, ObjectOwnedResponse, SuccessResponse,
UserAccessResponse,
},
tee::QuoteParams,
use cosmian_kms_utils::access::{
Access, AccessRightsObtainedResponse, ObjectOwnedResponse, SuccessResponse, UserAccessResponse,
};
use http::{HeaderMap, HeaderValue, StatusCode};
use josekit::{
@ -428,29 +423,6 @@ impl KmsRestClient {
self.get_no_ttlv("/access/obtained", None::<&()>).await
}
/// This operation requests the server to get the sgx quote or sev attestation report.
pub async fn get_attestation_report(
&self,
nonce: &[u8; 32],
) -> Result<Vec<u8>, RestClientError> {
let quote: String = self
.get_no_ttlv(
"/tee/attestation_report",
Some(&QuoteParams {
nonce: b64.encode(nonce),
}),
)
.await?;
Ok(b64.decode(quote.as_bytes())?)
}
/// This operation requests the server to get the HTTPS certificate.
pub async fn get_sgx_enclave_public_key(&self) -> Result<String, RestClientError> {
self.get_no_ttlv("/tee/sgx_enclave_public_key", None::<&()>)
.await
}
/// This operation requests the version of the server
pub async fn version(&self) -> Result<String, RestClientError> {
self.get_no_ttlv("/version", None::<&()>).await

View file

@ -2,13 +2,11 @@
//required to detect generic type in Serializer
#![feature(min_specialization)]
mod bootstrap_rest_client;
mod certificate_verifier;
mod error;
mod kms_rest_client;
mod result;
pub use bootstrap_rest_client::BootstrapRestClient;
pub use cosmian_kmip::kmip;
pub use error::RestClientError;
pub use kms_rest_client::KmsRestClient;

View file

@ -10,6 +10,7 @@ openssl = ["dep:openssl"]
pyo3 = ["dep:pyo3"]
[dependencies]
base58 = "0.2"
bitflags = { workspace = true }
chrono = { workspace = true }
cloudproof = { workspace = true }
@ -19,12 +20,13 @@ openssl = { workspace = true, optional = true }
pyo3 = { version = "0.20", optional = true }
serde = { workspace = true }
serde_json = { workspace = true }
sha3 = { version = "0.10", optional = true }
strum = { workspace = true }
thiserror = { workspace = true }
time = { workspace = true, features = ["formatting", "parsing", "serde"] }
tracing = { workspace = true }
zeroize = { workspace = true }
uuid = { version = "1.6.1", features = ["v4"] }
zeroize = { workspace = true }
[dev-dependencies]
cosmian_logger = { path = "../logger" }

27
crate/kmip/src/id.rs Normal file
View file

@ -0,0 +1,27 @@
use base58::ToBase58;
#[cfg(feature = "openssl")]
use openssl::hash::{hash, MessageDigest};
#[cfg(not(feature = "openssl"))]
use sha3::Digest;
use crate::error::KmipError;
/// Generate a unique ID from a byte slice.
///
/// Uses SHA3-256 hash function and base58 encoding
/// which generates file system friendly IDs.
/// # Arguments
/// * `bytes` - A byte slice
/// # Example
/// ```
/// use cosmian_kmip::id;
/// let id = id(b"Hello World!").unwrap();
/// assert_eq!(id, "F4RrBrbeAHQhQQCdoBNUJwSyk3iRr4eRsdULicFwer3p");
/// ```
pub fn id(bytes: &[u8]) -> Result<String, KmipError> {
#[cfg(feature = "openssl")]
let digest = hash(MessageDigest::sha3_256(), bytes)?.to_vec();
#[cfg(not(feature = "openssl"))]
let digest = sha3::Sha3_256::digest(bytes).to_vec();
Ok(digest.to_base58())
}

View file

@ -15,6 +15,7 @@ use crate::{
WrappingMethod,
},
},
openssl::pad_be_bytes,
};
/// A Key Block object is a structure used to encapsulate all of the information
@ -57,7 +58,24 @@ impl KeyBlock {
match &self.key_value.key_material {
KeyMaterial::ByteString(v) => Ok(Zeroizing::new(v.clone())),
KeyMaterial::TransparentSymmetricKey { key } => Ok(Zeroizing::new(key.clone())),
KeyMaterial::TransparentECPrivateKey { d, .. } => Ok(Zeroizing::new(d.to_bytes_be())),
KeyMaterial::TransparentECPrivateKey {
d,
recommended_curve,
} => {
let mut d_vec = d.to_bytes_be();
let privkey_size = match recommended_curve {
RecommendedCurve::P192 => 24,
RecommendedCurve::P224 => 28,
RecommendedCurve::P256 => 32,
RecommendedCurve::P384 => 48,
RecommendedCurve::P521 => 66,
RecommendedCurve::CURVE25519 | RecommendedCurve::CURVEED25519 => 32,
RecommendedCurve::CURVE448 | RecommendedCurve::CURVEED448 => 56,
_ => d_vec.len(),
};
pad_be_bytes(&mut d_vec, privkey_size);
Ok(Zeroizing::new(d_vec))
}
KeyMaterial::TransparentECPublicKey { q_string, .. } => {
Ok(Zeroizing::new(q_string.clone()))
}

View file

@ -459,12 +459,23 @@ impl<'de> Deserialize<'de> for TTLV {
/// doesn't provide such conversion.
#[must_use]
pub fn to_u32_digits(big_int: &BigUint) -> Vec<u32> {
// Since the KMS works with big-endian representation of bytearrays, casting
// a group of 4 bytes in big-endian u32 representation needs revert iter so
// that if you have a chunk [0, 12, 143, 239] you will do
// B = 239 + 143*2^8 + 12*2^16 + 0*2^24 which is the correct way to do. On
// top of that, if the number of bytes in `big_int` is not a multiple of 4,
// it will behave as if there were leading null bytes which is technically
// the case.
// In this case, using this to convert a BigUint to a Vec<u32> will not lose
// leading null bytes information which might be the case when an EC private
// key is legally generated with leading null bytes.
big_int
.to_bytes_be()
.chunks(4)
.map(|group_of_4_bytes| {
group_of_4_bytes
.iter()
.rev()
.fold(0, |acc, byte| (acc << 8) + u32::from(*byte))
})
.collect::<Vec<_>>()

View file

@ -5,6 +5,8 @@
#![feature(slice_take)]
pub mod error;
mod id;
pub use id::id;
pub mod kmip;
pub mod result;

View file

@ -3,5 +3,5 @@ mod private_key;
mod public_key;
pub use certificate::{kmip_certificate_to_openssl, openssl_certificate_to_kmip};
pub use private_key::{kmip_private_key_to_openssl, openssl_private_key_to_kmip};
pub use private_key::{kmip_private_key_to_openssl, openssl_private_key_to_kmip, pad_be_bytes};
pub use public_key::{kmip_public_key_to_openssl, openssl_public_key_to_kmip};

View file

@ -21,6 +21,12 @@ use crate::{
result::KmipResultHelper,
};
pub fn pad_be_bytes(bytes: &mut Vec<u8>, size: usize) {
while bytes.len() != size {
bytes.insert(0, 0);
}
}
/// Convert a KMIP Private key to openssl `PKey<Private>`
///
/// The supported `KeyFormatType` are:
@ -131,13 +137,22 @@ pub fn kmip_private_key_to_openssl(private_key: &Object) -> Result<PKey<Private>
recommended_curve,
} => match recommended_curve {
RecommendedCurve::CURVE25519 => {
PKey::private_key_from_raw_bytes(&d.to_bytes_be(), Id::X25519)?
let mut privkey_vec = d.to_bytes_be();
// 32 is privkey size on x25519.
pad_be_bytes(&mut privkey_vec, 32);
PKey::private_key_from_raw_bytes(&privkey_vec, Id::X25519)?
}
RecommendedCurve::CURVE448 => {
PKey::private_key_from_raw_bytes(&d.to_bytes_be(), Id::X448)?
let mut privkey_vec = d.to_bytes_be();
// 56 is privkey size on x448.
pad_be_bytes(&mut privkey_vec, 56);
PKey::private_key_from_raw_bytes(&privkey_vec, Id::X448)?
}
RecommendedCurve::CURVEED25519 => {
PKey::private_key_from_raw_bytes(&d.to_bytes_be(), Id::ED25519)?
let mut privkey_vec = d.to_bytes_be();
// 32 is privkey size on ed25519.
pad_be_bytes(&mut privkey_vec, 32);
PKey::private_key_from_raw_bytes(&privkey_vec, Id::ED25519)?
}
other => ec_private_key_from_scalar(d, other)?,
},
@ -160,16 +175,20 @@ fn ec_private_key_from_scalar(
scalar: &BigUint,
curve: &RecommendedCurve,
) -> Result<PKey<Private>, KmipError> {
let nid = match curve {
RecommendedCurve::P256 => Nid::X9_62_PRIME256V1,
RecommendedCurve::P192 => Nid::X9_62_PRIME192V1,
RecommendedCurve::P224 => Nid::SECP224R1,
RecommendedCurve::P384 => Nid::SECP384R1,
RecommendedCurve::P521 => Nid::SECP521R1,
let (nid, privkey_size) = match curve {
RecommendedCurve::P256 => (Nid::X9_62_PRIME256V1, 32),
RecommendedCurve::P192 => (Nid::X9_62_PRIME192V1, 24),
RecommendedCurve::P224 => (Nid::SECP224R1, 28),
RecommendedCurve::P384 => (Nid::SECP384R1, 48),
RecommendedCurve::P521 => (Nid::SECP521R1, 66),
x => kmip_bail!("Unsupported curve: {:?} in this KMIP implementation", x),
};
let big_num_context = BigNumContext::new()?;
let mut scalar_vec = scalar.to_bytes_be();
pad_be_bytes(&mut scalar_vec, privkey_size);
let scalar = BigNum::from_slice(scalar.to_bytes_be().as_slice())?;
let ec_group = EcGroup::from_curve_name(nid)?;
let mut ec_public_key = EcPoint::new(&ec_group)?;
ec_public_key.mul_generator(&ec_group, &scalar, &big_num_context)?;
@ -405,7 +424,7 @@ pub fn openssl_private_key_to_kmip(
#[cfg(test)]
mod tests {
use num_bigint_dig::BigUint;
use openssl::{
bn::BigNum,
ec::{EcGroup, EcKey},
@ -419,7 +438,10 @@ mod tests {
kmip_objects::Object,
kmip_types::{KeyFormatType, RecommendedCurve},
},
openssl::{kmip_private_key_to_openssl, private_key::openssl_private_key_to_kmip},
openssl::{
kmip_private_key_to_openssl,
private_key::{openssl_private_key_to_kmip, pad_be_bytes},
},
};
#[test]
@ -638,10 +660,14 @@ mod tests {
_ => panic!("Invalid key block"),
};
assert_eq!(recommended_curve, RecommendedCurve::P256);
let mut privkey_vec = d.to_bytes_be();
// 32 is privkey size on P-256.
pad_be_bytes(&mut privkey_vec, 32);
let private_key_ = PKey::from_ec_key(
EcKey::from_private_components(
&ec_group,
&BigNum::from_slice(d.to_bytes_be().as_slice()).unwrap(),
&BigNum::from_slice(privkey_vec.as_slice()).unwrap(),
&ec_public_key,
)
.unwrap(),
@ -712,11 +738,27 @@ mod tests {
_ => panic!("Invalid key block"),
};
assert_eq!(recommended_curve, RecommendedCurve::CURVE25519);
let private_key_ = PKey::private_key_from_raw_bytes(&d.to_bytes_be(), Id::X25519).unwrap();
let mut privkey_vec = d.to_bytes_be();
// 32 is privkey size on X25519.
pad_be_bytes(&mut privkey_vec, 32);
let private_key_ = PKey::private_key_from_raw_bytes(&privkey_vec, Id::X25519).unwrap();
assert_eq!(private_key_.id(), Id::X25519);
assert_eq!(private_key_.bits(), 253);
let private_key_ = kmip_private_key_to_openssl(&object_).unwrap();
assert_eq!(private_key_.id(), Id::X25519);
assert_eq!(private_key_.bits(), 253);
}
#[test]
fn test_conversion_privkey_null_first_byte() {
let key = [
0, 113, 8, 182, 184, 86, 82, 102, 195, 88, 8, 230, 119, 254, 2, 177, 228, 135, 20, 247,
106, 133, 91, 78, 125, 44, 57, 70, 202, 154, 25, 243,
];
let a = BigUint::from_bytes_be(&key);
let mut key_vec = a.to_bytes_be();
pad_be_bytes(&mut key_vec, 32);
assert_eq!(key_vec, key);
}
}

View file

@ -13,19 +13,18 @@ insecure = []
timeout = []
# Enable all the feature even insecure but timeout
# To run in a test environment using a real domain name and enclaves without the rest api limit of let's encrypt
staging = ["insecure"]
# Enable FIPS module feature build. KMS builds in FIPS mode when this is enabled.
fips = ["cosmian_kms_utils/fips"]
# No features (insecure, timeout or staging) and contain non FIPS approved libraries (e.g. Rust Crypto)
default = []
prod = []
[dependencies]
acme-lib = { git = "https://github.com/Cosmian/acme-lib", branch = "master" }
actix-cors = "0.6"
actix-files = "0.6"
actix-identity = "0.6"
actix-multipart = "0.6"
actix-rt = { workspace = true }
actix-service = "2.0"
actix-tls = "3.1"
@ -49,20 +48,18 @@ josekit = { workspace = true }
lazy_static = "1.4"
mime = "0.3"
openssl = { workspace = true }
ratls = { workspace = true }
rawsql = "0.1"
redis = { workspace = true }
reqwest = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
sqlx = { version = "0.7.3", default-features = false, features = [
"json",
"runtime-tokio-native-tls",
"mysql",
"postgres",
"sqlite",
"json",
"runtime-tokio-native-tls",
"mysql",
"postgres",
"sqlite",
] }
tee_attestation = { workspace = true }
thiserror = { workspace = true }
time = { workspace = true, features = ["local-offset", "formatting"] }
# this version of tokio should be the same as the one used in actix-web

View file

@ -6,10 +6,7 @@ The KMS server provides several features which can be enabled at compilation tim
| Feature | Description | Staging | Prod 🔥 |
|----------|-------------------------------------------------------------------------------------------------------------------|---------|---------|
| auth | Enable authentication. If disabled, multi-user is not supported | ✅ | ✅ |
| enclave | Enable the ability to run inside an enclave | ✅ | ✅ |
| https | Enable https in the KMS in order to encrypt query between client and the KMS. If disabled, it uses http | ✅ | ✅ |
| insecure | Do not verify auth0 token expiration date and https ssl is self-signed (to avoid to be banned by letsencrypt) | ✅ | |
| insecure | Do not verify auth0 token expiration date | ✅ | |
| timeout | The binary will stop (and won't be able to start again) after a period of time, starting from date of compilation | | |
**Caption**:
@ -32,8 +29,6 @@ cargo build --no-default-features
For staging environment, you can use `--features=staging --no-default-features`. It will tell the server:
- to not verify the expiration of OAuth2 tokens if `KMS_JWT_ISSUER_URI` is set.
- to use HTTPS connection with unsecure SSL certificates (it will play anyway all the process to get a valid certificates and starts a HTTPS server)
- to be runnable only inside an enclave
### Timeout feature
@ -64,20 +59,16 @@ cosmian_kms_server -h
A server for development can be quickly run as follow (with sqlite):
```sh
cargo run --no-default-features -- --public-path /tmp --shared-path /tmp --private-path /tmp
cargo run --no-default-features -- --tmp-path /tmp
```
or:
```sh
export KMS_ENCLAVE_DIR_PATH=/tmp
export KMS_CERTBOT_SSL_PATH=/tmp
export KMS_SQLITE_PATH=/tmp
cargo run --no-default-features
```
The 3 parameters `public-path`, `shared-path` and `private-path` are related to certificate generation in the Secure Enclave. Outside of the enclave, the value of these parameters can be set to `/tmp`.
## Configure the authentication
The KMS server relies on an OAuth2 authentication provided by Auth0 to authenticate the user.
@ -131,86 +122,6 @@ cargo install cargo-make
cargo make rust-tests
```
## Production / Running inside a Secure Enclave
> You can run the KMS on a non-sgx environment for production. In that case, the server will have the same behavior with a lower security level and with some routes disabled for the user.
At _Cosmian_, for production, the architecture and the security rely on secure enclaves. With no feature flag specified during the building process, the generated binary targets the production environment.
![Production](./resources/production.drawio.svg)
To set up the SGX enclave to run the KMS server, please refer to the dedicated [Readme](../../sgx/README.md)
**Mandatory**: all KMS source codes are fully open source.
### HTTPS
The REST API server of the KMS server is launched into a secure enclave. It accepts HTTPS connection only.
To be sure that _Cosmian_ can't decrypt the HTTPS flow (in a MITM scenario), the SSL certificate is generated inside the enclave. The private key is not exposed to the host in plain-text.
**How it works?**
The KMS will ask a certificate to _Let's Encrypt_. To do so, it starts a temporary HTTP server to play the HTTP-challenge.
After getting the certificate, it stores them on disk: the private key is encrypted and only readable inside the enclave.
Then, the real HTTPS server is started using this latter and the user can now query the KMS.
If the initialized KMS is manually restarted while running:
- if the private key can be read, the HTTPS server is restarted immediately
- otherwise, the certification process will raise an exception and the server won't start
At a point, the certificate will be renew automatically and the HTTPS server will be restarted immediately and automatically.
If an error occurs during the certification process, the server stops.
### The database
The KMS database is located in the same secure enclave as multiple sqlcipher databases. Let's call _group_ a set of user sharing the same database.
As a consequence:
- These users share the same key to decrypt the database
- They can share KMS objects between each other
A single KMS instance can manage several groups, that is to say, several databases.
The key to decrypt a database is firstly generated by the KMS and returned to the user who has queried the creation of a new _group_. The KMS will not save this key. That is to say, _Cosmian_ can't decrypt the database apart from the users queries.
To reply to the user queries, the KMS is expecting the user to send the key with the query.
If the initialized KMS is manually restarted while running:
- the KMS won't be able to read the databases. It will wait for the user to resend the key with its next query.
Because:
- the link between the KMS and the user is SSL-encrypted,
- the memory of the KMS is located inside the enclave,
- the ssl key material is located inside the enclave,
Then: _Cosmian_ can't get the database keys at any points.
### Update
Now, we have described how to initialize the KMS secrets and use them to communication with the end-user or the database, we will describe how it deals with these secrets when there is an update.
Let's remind that any modifications of the KMS source code, will generate a different binary. Therefore, the signature of that binary will be altered. As a consequence, any secrets stored in the KMS using `mr_enclave` won't be readable by the new version of the KMS. Besides, as said previously, _Cosmian_ doesn't know these secrets and can't initialize the new version of the KMS with these unknown previous secrets.
Let's describe how the migration of these various secrets happens.
To restart the KMS needs:
- The SSL keys and the public certificate. _Cosmian_ can't read them as the the new KMS. Therefore, the new KMS version will remove the previous keys and regenerate them. As a consequence, all new versions pushed by _Cosmian_ could be transparently known by any KMS user.
- The sqlcipher keys. These secrets are located in the user side. Therefore, the keys will be read from the users queries.
### Resilience & Redundancy
This part cover the following scenario: we lost the KMS server and the KMS database. As a consequence, we have lost the user data and the secrets. We wan't to avoid that scenario to occur by having some sort of a database backup and secrets backup to be able to restore them if needed.
Let's describe how _Cosmian_ deals with this concern:
- The HTTPS server can be lost. _Cosmian_ will start a new one in another machine. The `mr_enclave` key will be changed. As for the update process, the new KMS version will remove the previous SSL keys and regenerate them.
- The sqlcipher-encrypted databases are stored in plain-text on the host. It means that, if the user provides the sqlcipher key, a new KMS in another secure enclave can reload the database. The database files are written to a network volume. The replication of this volume is managed by Azure with a high level of redundancy.
## In-depth understanding
### Database

View file

@ -1,52 +0,0 @@
use clap::Args;
/// The configuration used by the bootstrap server.
///
/// The hostname is the same as the one used by the KMS server,
/// only the port can be changed.
#[derive(Debug, Args, Clone)]
pub struct BootstrapServerConfig {
/// Whether configuration should be finalized using a bootstrap server
#[clap(long, env("KMS_USE_BOOTSTRAP_SERVER"), default_value("false"))]
pub use_bootstrap_server: bool,
/// Subject as an RFC 4514 string for the RA-TLS certificate
/// in the bootstrap server
#[clap(
long,
env("KMS_BOOTSTRAP_SERVER_SUBJECT"),
default_value("CN=cosmian.kms,O=Cosmian Tech,C=FR,L=Paris,ST=Ile-de-France")
)]
pub bootstrap_server_subject: String,
/// Number of days before the certificate expires
#[clap(
long,
env("KMS_BOOTSTRAP_SERVER_EXPIRATION_DAYS"),
default_value("365")
)]
pub bootstrap_server_expiration_days: u64,
/// The bootstrap server may be started on a specific port,
/// The hostname will be that configured in --hostname
#[clap(long, env("KMS_BOOTSTRAP_SERVER_PORT"), default_value("9998"))]
pub bootstrap_server_port: u16,
/// Ensure RA-TLS is available and used.
/// The server will not start if this is not the case.
#[clap(long, env("KMS_ENSURE_RA_TLS"), default_value("false"))]
pub ensure_ra_tls: bool,
}
impl Default for BootstrapServerConfig {
fn default() -> Self {
Self {
use_bootstrap_server: false,
bootstrap_server_subject: "CN=cosmian.kms,O=Cosmian Tech,C=FR,L=Paris,ST=Ile-de-France"
.to_string(),
bootstrap_server_port: 9998,
bootstrap_server_expiration_days: 365,
ensure_ra_tls: false,
}
}
}

View file

@ -1,94 +0,0 @@
use std::{
fs,
path::{Path, PathBuf},
};
use clap::Args;
use tracing::info;
use super::WorkspaceConfig;
use crate::{core::certbot::Certbot, error::KmsError, kms_error, result::KResult};
#[derive(Debug, Args)]
pub struct HttpsCertbotConfig {
/// Enable TLS and use Let's Encrypt certbot to get a certificate
#[clap(long, required(false), env("KMS_USE_CERTBOT"), default_value = "false")]
pub use_certbot: bool,
/// Use TEE key generation to generate the certificate certificate (only available on tee). The value (hexadecimal) is a random salt used to derive a key from the TEE materials
#[clap(
long,
required(false),
env("KMS_CERTBOT_USE_TEE_KEY"),
default_value = None
)]
pub certbot_use_tee_key: Option<String>,
/// The hostname of the KMS HTTPS server
/// that will be used as the Common Name in the Let's Encrypt certificate
#[clap(
long,
env("KMS_CERTBOT_HOSTNAME"),
required(false),
required_if_eq("use_certbot", "true"),
default_value = ""
)]
pub certbot_hostname: String,
/// The email used during the Let's Encrypt certbot certification process
#[clap(
long,
env("KMS_CERTBOT_EMAIL"),
required(false),
required_if_eq("use_certbot", "true"),
default_value = ""
)]
pub certbot_email: String,
/// The folder where the KMS will store the SSL material created by certbot
///
/// A relative path is taken relative to the root_data_path
#[clap(long, env = "KMS_CERTBOT_SSL_PATH", default_value = "./certbot-ssl")]
pub certbot_ssl_path: PathBuf,
}
impl Default for HttpsCertbotConfig {
fn default() -> Self {
Self {
use_certbot: false,
certbot_use_tee_key: None,
certbot_email: String::new(),
certbot_hostname: String::new(),
certbot_ssl_path: std::env::temp_dir(),
}
}
}
impl HttpsCertbotConfig {
pub fn init(&self, workspace: &WorkspaceConfig) -> KResult<Certbot> {
let certbot_ssl_path = workspace.finalize_directory(&self.certbot_ssl_path)?;
let http_root_path = workspace.tmp_path.join("html");
if !Path::new(&http_root_path).exists() {
info!("Creating {:?}...", http_root_path);
fs::create_dir_all(&http_root_path)?;
}
Ok(Certbot::new(
self.certbot_email.clone(),
self.certbot_hostname.clone(),
std::fs::canonicalize(http_root_path).map_err(|e| kms_error!(e))?,
std::fs::canonicalize(certbot_ssl_path).map_err(|e| kms_error!(e))?,
if let Some(salt) = &self.certbot_use_tee_key {
Some(hex::decode(salt).map_err(|_| {
KmsError::ConversionError(
"`certbot_use_tee_key` value is not a hexadecimal string".to_string(),
)
})?)
} else {
None
},
))
}
}

View file

@ -1,12 +1,8 @@
use std::fmt::{self};
use clap::Parser;
use tee_attestation::is_running_inside_tee;
use super::{
BootstrapServerConfig, DBConfig, HttpConfig, HttpsCertbotConfig, JWEConfig, JwtAuthConfig,
TeeConfig, WorkspaceConfig,
};
use super::{DBConfig, HttpConfig, JWEConfig, JwtAuthConfig, WorkspaceConfig};
#[derive(Parser, Default)]
#[clap(version, about, long_about = None)]
@ -20,15 +16,9 @@ pub struct ClapConfig {
#[clap(flatten)]
pub auth: JwtAuthConfig,
#[clap(flatten)]
pub bootstrap_server: BootstrapServerConfig,
#[clap(flatten)]
pub workspace: WorkspaceConfig,
#[clap(flatten)]
pub certbot_https: HttpsCertbotConfig,
/// The default username to use when no authentication method is provided
#[clap(long, env = "KMS_DEFAULT_USERNAME", default_value = "admin")]
pub default_username: String,
@ -41,9 +31,6 @@ pub struct ClapConfig {
#[clap(flatten)]
pub jwe: JWEConfig,
#[clap(flatten)]
pub tee: TeeConfig,
#[clap(long, env = "KMS_GOOGLE_CSE_KACLS_URL")]
/// This setting enables the Google Workspace Client Side Encryption feature of this KMS server.
///
@ -56,39 +43,13 @@ pub struct ClapConfig {
impl fmt::Debug for ClapConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut x = f.debug_struct("");
let x = if self.bootstrap_server.use_bootstrap_server {
x.field(
"bootstrap server port",
&self.bootstrap_server.bootstrap_server_port,
)
.field(
"bootstrap server subject",
&self.bootstrap_server.bootstrap_server_subject,
)
.field(
"bootstrap server days before expiration",
&self.bootstrap_server.bootstrap_server_expiration_days,
)
} else {
&mut x
};
let x = x.field("db", &self.db);
let x = if self.auth.jwt_issuer_uri.is_some() {
x.field("auth0", &self.auth)
} else {
x
};
let x = if is_running_inside_tee() {
x.field("tee", &self.tee)
} else {
x
};
let x = x.field("KMS http", &self.http);
let x = if self.certbot_https.use_certbot {
x.field("certbot", &self.certbot_https)
} else {
x
};
let x = x.field("workspace", &self.workspace);
let x = x.field("default username", &self.default_username);
let x = x.field("force default username", &self.force_default_username);

View file

@ -21,8 +21,6 @@ pub struct DBConfig {
/// A key must be supplied on every call
/// - redis-findex: a Redis database with encrypted data and encrypted indexes thanks to Findex.
/// The Redis url must be provided, as well as the redis-master-password and the redis-findex-label
///
/// The database configuration can be securely provided via the bootstrap server. Check the documentation.
#[clap(
long,
env("KMS_DATABASE_TYPE"),
@ -144,7 +142,6 @@ impl DBConfig {
///
/// # Parameters
/// - `workspace`: The workspace configuration used to determine the public and shared paths
/// - `use_bootstrap_server`: Whether the bootstrap server should be used to configure the database
///
/// # Returns
/// - The DB parameters
@ -152,11 +149,7 @@ impl DBConfig {
/// # Errors
/// - If both Postgres and MariaDB/MySQL URL are set
/// - If `SQLCipher` is set along with Postgres or MariaDB/MySQL URL
pub fn init(
&self,
workspace: &WorkspaceConfig,
use_bootstrap_server: bool,
) -> KResult<Option<DbParams>> {
pub fn init(&self, workspace: &WorkspaceConfig) -> KResult<Option<DbParams>> {
Ok(if let Some(database_type) = &self.database_type {
Some(match database_type.as_str() {
"postgresql" => {
@ -199,9 +192,6 @@ impl DBConfig {
}
unknown => kms_bail!("Unknown database type: {unknown}"),
})
} else if use_bootstrap_server {
// That is fine: the bootstrap server will be used to configure the database
None
} else {
// No database configuration provided; use the default config
let path = workspace.finalize_directory(&self.sqlite_path)?;

View file

@ -8,19 +8,15 @@ pub struct HttpConfig {
#[clap(long, env = "KMS_PORT", default_value = "9998")]
pub port: u16,
/// The KMS server (and bootstrap server) hostname
/// The KMS server hostname
#[clap(long, env = "KMS_HOSTNAME", default_value = "0.0.0.0")]
pub hostname: String,
/// The KMS server optional PKCS#12 Certificates and Key file. If provided, this will start the server in HTTPS mode.
///
/// The PKCS#12 can be securely provided via the bootstrap server. Check the documentation.
#[clap(long, env = "KMS_HTTPS_P12_FILE")]
pub https_p12_file: Option<PathBuf>,
/// The password to open the PKCS#12 Certificates and Key file
///
/// The PKCS#12 password can be securely provided via the bootstrap server. Check the documentation.
#[clap(long, env = "KMS_HTTPS_P12_PASSWORD", default_value = "")]
pub https_p12_password: String,

View file

@ -1,19 +1,13 @@
mod bootstrap_server_config;
mod certbot_https;
mod clap_config;
mod db;
mod http_config;
mod jwe;
mod jwt_auth_config;
mod tee;
mod workspace;
pub use bootstrap_server_config::BootstrapServerConfig;
pub use certbot_https::HttpsCertbotConfig;
pub use clap_config::ClapConfig;
pub use db::DBConfig;
pub use http_config::HttpConfig;
pub use jwe::{JWEConfig, Jwk};
pub use jwt_auth_config::JwtAuthConfig;
pub use tee::TeeConfig;
pub use workspace::WorkspaceConfig;

View file

@ -1,60 +0,0 @@
use std::path::{Path, PathBuf};
use clap::Args;
use tee_attestation::is_running_inside_tee;
use super::WorkspaceConfig;
use crate::{config::params::TeeParams, kms_bail, result::KResult};
#[derive(Debug, Args)]
pub struct TeeConfig {
/// The directory where the public key or other required files are located
/// This path should not be encrypted by the enclave and should be directly readable from it
///
/// A relative path is taken relative to the `root_data_path` (see `WorkspaceConfig` struct)
#[clap(long, env("KMS_TEE_DIR_PATH"), default_value("./tee"))]
pub tee_dir_path: PathBuf,
/// The filename of the public key for SGX
#[clap(long, env("KMS_SGX_PUBLIC_SIGNER_KEY_FILENAME"))]
pub sgx_public_signer_key_filename: Option<PathBuf>,
}
impl Default for TeeConfig {
fn default() -> Self {
Self {
tee_dir_path: PathBuf::from("./tee"),
sgx_public_signer_key_filename: None,
}
}
}
impl TeeConfig {
pub fn init(&self, workspace: &WorkspaceConfig) -> KResult<TeeParams> {
if !is_running_inside_tee() {
let default = Self::default();
// these paths are never used nor created
return Ok(TeeParams {
sgx_public_signer_key: default.sgx_public_signer_key_filename,
})
}
if let Some(sgx_public_signer_key_filename) = &self.sgx_public_signer_key_filename {
// finalize the enclave dir path
let enclave_dir_path = workspace.finalize_directory(&self.tee_dir_path)?;
let sgx_public_signer_key = enclave_dir_path.join(sgx_public_signer_key_filename);
if !Path::new(&sgx_public_signer_key).exists() {
kms_bail!("Can't find '{sgx_public_signer_key:?}' as sgx_public_signer_key");
}
return Ok(TeeParams {
sgx_public_signer_key: Some(sgx_public_signer_key),
})
}
Ok(TeeParams {
sgx_public_signer_key: None,
})
}
}

View file

@ -2,4 +2,4 @@ mod command_line;
mod params;
pub use command_line::*;
pub use params::{DbParams, HttpParams, ServerParams, TeeParams};
pub use params::{DbParams, HttpParams, ServerParams};

View file

@ -1,36 +1,19 @@
use std::{
fmt,
fs::File,
io::Read,
sync::{Arc, Mutex},
};
use std::{fmt, fs::File, io::Read};
use openssl::pkcs12::{ParsedPkcs12_2, Pkcs12};
use crate::{
config::{command_line::HttpsCertbotConfig, ClapConfig, WorkspaceConfig},
core::certbot::Certbot,
result::KResult,
};
use crate::{config::ClapConfig, result::KResult};
/// The HTTP parameters of the API server
pub enum HttpParams {
Certbot(Arc<Mutex<Certbot>>),
Https(ParsedPkcs12_2),
Http,
}
impl HttpParams {
pub fn try_from(config: &ClapConfig, workspace: &WorkspaceConfig) -> KResult<Self> {
// certbot is the priority is that is provided
if config.certbot_https.use_certbot {
let certbot = Arc::new(Mutex::new(HttpsCertbotConfig::init(
&config.certbot_https,
workspace,
)?));
Ok(Self::Certbot(certbot))
// else start in HTTPS mode if a PKCS#12 file is provided
} else if let Some(p12_file) = &config.http.https_p12_file {
pub fn try_from(config: &ClapConfig) -> KResult<Self> {
// start in HTTPS mode if a PKCS#12 file is provided
if let Some(p12_file) = &config.http.https_p12_file {
// Open and read the file into a byte vector
let mut file = File::open(p12_file)?;
let mut der_bytes = Vec::new();
@ -48,7 +31,7 @@ impl HttpParams {
#[must_use]
pub fn is_running_https(&self) -> bool {
match self {
Self::Certbot(_) | Self::Https(_) => true,
Self::Https(_) => true,
Self::Http => false,
}
}
@ -57,7 +40,6 @@ impl HttpParams {
impl fmt::Debug for HttpParams {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HttpParams::Certbot(certbot) => f.debug_tuple("Certbot").field(certbot).finish(),
HttpParams::Https(ParsedPkcs12_2 {
cert: Some(x509), ..
}) => f

View file

@ -1,13 +1,7 @@
mod db_params;
mod http_params;
mod server_params;
mod tee_params;
pub use db_params::DbParams;
pub use http_params::HttpParams;
pub use server_params::ServerParams;
pub use tee_params::TeeParams;
use super::command_line::BootstrapServerConfig;
pub type BootstrapServerParams = BootstrapServerConfig;

View file

@ -1,15 +1,9 @@
use std::{
fmt::{self},
fs::File,
io::Read,
path::PathBuf,
};
use std::{fmt, fs::File, io::Read, path::PathBuf};
use alcoholic_jwt::JWKS;
use openssl::x509::X509;
use tee_attestation::is_running_inside_tee;
use super::{BootstrapServerParams, DbParams, HttpParams, TeeParams};
use super::{DbParams, HttpParams};
use crate::{
config::{command_line::JWEConfig, ClapConfig},
kms_bail,
@ -38,7 +32,7 @@ pub struct ServerParams {
/// but always use the default username instead of the one provided by the authentication method
pub force_default_username: bool,
/// The DB parameters may be supplied on the command line or via the bootstrap server
/// The DB parameters may be supplied on the command line
pub db_params: Option<DbParams>,
/// Whether to clear the database on start
@ -48,26 +42,11 @@ pub struct ServerParams {
pub port: u16,
// /// The provided PKCS#12 when HTTPS is enabled
// pub server_pkcs_12: Option<ParsedPkcs12_2>,
// /// The certbot engine if certbot is enabled
// pub certbot: Option<Arc<Mutex<Certbot>>>,
pub http_params: HttpParams,
/// The tee parameters when running inside a tee
pub tee_params: TeeParams,
/// The certificate used to verify the client TLS certificates
/// used for authentication
pub verify_cert: Option<X509>,
/// Use a bootstrap server (inside a tee for instance)
pub bootstrap_server_params: BootstrapServerParams,
/// Ensure RA-TLS is available and used.
/// The server will not start if this is not the case.
pub ensure_ra_tls: bool,
pub client_cert: Option<X509>,
/// This setting enables the Google Workspace Client Side Encryption feature of this KMS server.
///
@ -83,7 +62,7 @@ impl ServerParams {
let workspace = conf.workspace.init()?;
// The HTTP/HTTPS parameters
let http_params = HttpParams::try_from(conf, &workspace)?;
let http_params = HttpParams::try_from(conf)?;
// Should we verify the client TLS certificates?
let verify_cert = if let Some(authority_cert_file) = &conf.http.authority_cert_file {
@ -104,19 +83,14 @@ impl ServerParams {
jwt_issuer_uri: conf.auth.jwt_issuer_uri.clone(),
jwe_config: conf.jwe.clone(),
jwt_audience: conf.auth.jwt_audience.clone(),
db_params: conf
.db
.init(&workspace, conf.bootstrap_server.use_bootstrap_server)?,
db_params: conf.db.init(&workspace)?,
clear_db_on_start: conf.db.clear_database,
hostname: conf.http.hostname.clone(),
port: conf.http.port,
http_params: HttpParams::try_from(conf, &workspace)?,
tee_params: conf.tee.init(&workspace)?,
http_params: HttpParams::try_from(conf)?,
default_username: conf.default_username.clone(),
force_default_username: conf.force_default_username,
verify_cert,
bootstrap_server_params: conf.bootstrap_server.clone(),
ensure_ra_tls: conf.bootstrap_server.ensure_ra_tls,
client_cert: verify_cert,
google_cse_kacls_url: conf.google_cse_kacls_url.clone(),
};
Ok(server_conf)
@ -137,31 +111,6 @@ impl ServerParams {
impl fmt::Debug for ServerParams {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut x = f.debug_struct("");
let x = if self.bootstrap_server_params.use_bootstrap_server {
x.field(
"bootstrap server url",
&format!(
"https://{}:{}",
&self.hostname, &self.bootstrap_server_params.bootstrap_server_port
),
)
.field(
"bootstrap server subject",
&self.bootstrap_server_params.bootstrap_server_subject,
)
.field(
"bootstrap server days before expiration",
&self
.bootstrap_server_params
.bootstrap_server_expiration_days,
)
.field(
"bootstrap server ensure RA-TLS",
&self.bootstrap_server_params.ensure_ra_tls,
)
} else {
&mut x
};
let x = x
.field(
"kms_url",
@ -185,7 +134,7 @@ impl fmt::Debug for ServerParams {
} else {
x
};
let x = if let Some(verify_cert) = &self.verify_cert {
let x = if let Some(verify_cert) = &self.client_cert {
x.field("verify_cert CN", verify_cert.subject_name())
} else {
x
@ -194,11 +143,6 @@ impl fmt::Debug for ServerParams {
.field("default_username", &self.default_username)
.field("force_default_username", &self.force_default_username);
let x = x.field("http_params", &self.http_params);
let x = if is_running_inside_tee() {
x.field("tee_params", &self.tee_params)
} else {
x
};
x.finish()
}
}
@ -220,10 +164,7 @@ impl Clone for ServerParams {
hostname: self.hostname.clone(),
port: self.port,
http_params: HttpParams::Http,
tee_params: self.tee_params.clone(),
verify_cert: self.verify_cert.clone(),
bootstrap_server_params: self.bootstrap_server_params.clone(),
ensure_ra_tls: self.ensure_ra_tls,
client_cert: self.client_cert.clone(),
google_cse_kacls_url: self.google_cse_kacls_url.clone(),
}
}

View file

@ -1,7 +0,0 @@
use std::path::PathBuf;
#[derive(Clone, Debug)]
pub struct TeeParams {
// contains the path to the sgx signer public key
pub sgx_public_signer_key: Option<PathBuf>,
}

View file

@ -1,249 +0,0 @@
use std::{
fmt, fs,
path::{Path, PathBuf},
};
use acme_lib::{
create_p384_key, persist::FilePersist, Account, Certificate, Directory, DirectoryUrl,
};
#[cfg(target_os = "linux")]
use openssl::{
bn::{BigNum, BigNumContext},
ec::{EcGroup, EcKey, EcPoint},
nid::Nid,
pkey::{PKey, Private},
x509::X509,
};
#[cfg(not(target_os = "linux"))]
use openssl::{
pkey::{PKey, Private},
x509::X509,
};
use crate::{error::KmsError, kms_bail, result::KResult};
#[derive(Clone)]
pub struct Certbot {
pub days_threshold_before_renew: i64,
pub email: String,
pub common_name: String,
pub http_root_path: PathBuf,
pub keys_path: PathBuf,
pub use_tee_key: Option<Vec<u8>>,
account: Option<Account<FilePersist>>,
certificate: Option<Certificate>,
}
impl fmt::Debug for Certbot {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Certbot")
.field(
"days_threshold_before_renew",
&self.days_threshold_before_renew,
)
.field("email", &self.email)
.field("common name", &self.common_name)
.field("http_root_path", &self.http_root_path)
.field("keys_path", &self.keys_path)
.finish()
}
}
impl Default for Certbot {
fn default() -> Self {
Self::new(
String::new(),
String::new(),
PathBuf::from(""),
PathBuf::from(""),
None,
)
}
}
impl Certbot {
pub fn new(
email: String,
common_name: String,
http_root_path: PathBuf,
keys_path: PathBuf,
use_tee_key: Option<Vec<u8>>,
) -> Self {
Self {
days_threshold_before_renew: 15,
email,
common_name,
http_root_path,
keys_path,
use_tee_key,
account: None,
certificate: None,
}
}
pub fn init(&mut self) -> KResult<()> {
#[cfg(feature = "insecure")]
let url = DirectoryUrl::LetsEncryptStaging;
#[cfg(not(feature = "insecure"))]
let url = DirectoryUrl::LetsEncrypt;
// Save/load keys and certificates to current dir.
let persist = FilePersist::new(&self.keys_path);
// Create a directory entrypoint.
let dir = Directory::from_url(persist, url)?;
let acc = dir.account(&self.email)?;
self.certificate = acc.certificate(&self.common_name)?;
self.account = Some(acc);
Ok(())
}
// Check if the certificate exists and is valid
pub fn check(&self) -> bool {
match &self.certificate {
Some(certificate) => certificate.valid_days_left() > self.days_threshold_before_renew,
_ => false,
}
}
// Return the number of days before the certificate expired
pub fn get_days_before_expiration(&self) -> KResult<i64> {
match &self.certificate {
Some(certificate) => Ok(certificate.valid_days_left()),
_ => Ok(0),
}
}
// Return the number of days before the certificate has to be renewed
pub fn get_days_before_renew(&self) -> KResult<i64> {
Ok(self.get_days_before_expiration()? - self.days_threshold_before_renew)
}
// Get the certificate as standard OpenSSL objects
pub fn get_cert(&self) -> KResult<(PKey<Private>, Vec<X509>)> {
if let Some(certificate) = &self.certificate {
return Ok((
PKey::private_key_from_pem(certificate.private_key().as_bytes())?,
X509::stack_from_pem(certificate.certificate().as_bytes())?,
))
}
kms_bail!("Certificate can't be found...");
}
// Get the certificate as ACME objects
pub fn get_raw_cert(&self) -> KResult<(&str, &str)> {
if let Some(certificate) = &self.certificate {
return Ok((certificate.private_key(), certificate.certificate()))
}
kms_bail!("Certificate can't be found...");
}
#[cfg(target_os = "linux")]
pub fn generate_private_key(&self) -> KResult<PKey<Private>> {
if let Some(salt) = &self.use_tee_key {
let key = tee_attestation::get_key(Some(salt))?;
let private_number = BigNum::from_slice(&key)?;
let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)?;
let mut public_point = EcPoint::new(&group)?;
let ctx = BigNumContext::new()?;
public_point.mul_generator(&group, &private_number, &ctx)?;
let pri_key_ec =
EcKey::from_private_components(&group, &private_number, &public_point)?;
Ok(PKey::from_ec_key(pri_key_ec)?)
} else {
Ok(create_p384_key())
}
}
#[cfg(not(target_os = "linux"))]
pub fn generate_private_key(&self) -> KResult<PKey<Private>> {
Ok(create_p384_key())
}
pub fn request_cert(&mut self) -> KResult<()> {
let pkey_pri = self.generate_private_key()?;
let acc = self
.account
.as_ref()
.ok_or_else(|| KmsError::ServerError("Account shouldn't be None".to_string()))?;
// Order a new TLS certificate for a domain.
let mut ord_new = acc.new_order(&self.common_name, &[])?;
let target = Path::new(&self.http_root_path).join(".well-known/acme-challenge/");
let target_parent = Path::new(&self.http_root_path).join(".well-known");
// If the ownership of the domain(s) has already been
// authorized in a previous order, you might be able to
// skip validation. The ACME API provider decides.
let ord_csr = loop {
// are we done?
if let Some(ord_csr) = ord_new.confirm_validations() {
break ord_csr
}
// Get the possible authorizations (for a single domain
// this will only be one element).
let auths = ord_new.authorizations()?;
// For HTTP, the challenge is a text file that needs to
// be placed in your web server's root:
//
// /var/www/.well-known/acme-challenge/<token>
//
// The important thing is that it's accessible over the
// web for the domain(s) you are trying to get a
// certificate for:
//
// http://mydomain.io/.well-known/acme-challenge/<token>
let chall = auths[0].http_challenge();
// The token is the filename.
let token = chall.http_token();
// The proof is the contents of the file
let proof = chall.http_proof();
// Update my web server
fs::create_dir_all(&target)?;
fs::write(target.join(token), proof).expect("Unable to write the token file");
// After the file is accessible from the web,
// this tells the ACME API to start checking the
// existence of the proof.
//
// The order at ACME will change status to either
// confirm ownership of the domain, or fail due to the
// not finding the proof. To see the change, we poll
// the API with 5000 milliseconds wait between.
chall.validate(5000)?;
// Update the state against the ACME API.
ord_new.refresh()?;
// Clean the .well-known
#[allow(clippy::needless_borrow)]
fs::remove_dir_all(&target_parent)?;
};
// Submit the CSR. This causes the ACME provider to enter a
// state of "processing" that must be polled until the
// certificate is either issued or rejected. Again we poll
// for the status change.
let ord_cert = ord_csr.finalize_pkey(pkey_pri, 5000)?;
// Now download the certificate. Also stores the cert in
// the persistence.
self.certificate = Some(ord_cert.download_and_save_cert()?);
Ok(())
}
}

View file

@ -18,18 +18,25 @@ use cosmian_kmip::{
},
openssl::{kmip_private_key_to_openssl, kmip_public_key_to_openssl},
};
#[cfg(not(feature = "fips"))]
use cosmian_kms_utils::crypto::curve_25519::operation::create_p192_key_pair;
use cosmian_kms_utils::{
access::ExtraDatabaseParams,
crypto::{
cover_crypt::{decryption::CovercryptDecryption, encryption::CoverCryptEncryption},
curve_25519::operation::{create_ed25519_key_pair, create_x25519_key_pair},
curve_25519::operation::{
create_ed25519_key_pair, create_p224_key_pair, create_p256_key_pair,
create_p384_key_pair, create_p521_key_pair, create_x25519_key_pair,
},
hybrid_encryption::{HybridDecryptionSystem, HybridEncryptionSystem},
symmetric::{create_symmetric_key, AesGcmSystem},
},
tagging::{check_user_tags, get_tags, remove_tags},
DecryptionSystem, EncryptionSystem, KeyPair,
};
use tracing::{debug, log::warn, trace};
#[cfg(not(feature = "fips"))]
use tracing::warn;
use tracing::{debug, trace};
use zeroize::Zeroize;
use super::{cover_crypt::create_user_decryption_key, KMS};
@ -382,17 +389,32 @@ impl KMS {
.cryptographic_domain_parameters
.unwrap_or_default();
match dp.recommended_curve.unwrap_or_default() {
// TODO - #[cfg(not(feature = "fips"))]
RecommendedCurve::CURVE25519 => {
let mut rng = self.rng.lock().expect("RNG lock poisoned");
create_x25519_key_pair(&mut *rng, private_key_uid, public_key_uid)
create_x25519_key_pair(private_key_uid, public_key_uid)
}
#[cfg(not(feature = "fips"))]
RecommendedCurve::P192 => create_p192_key_pair(private_key_uid, public_key_uid),
RecommendedCurve::P224 => create_p224_key_pair(private_key_uid, public_key_uid),
RecommendedCurve::P256 => create_p256_key_pair(private_key_uid, public_key_uid),
RecommendedCurve::P384 => create_p384_key_pair(private_key_uid, public_key_uid),
RecommendedCurve::P521 => create_p521_key_pair(private_key_uid, public_key_uid),
#[cfg(not(feature = "fips"))]
RecommendedCurve::CURVEED25519 => {
warn!(
"An Edwards Keypair on curve 25519 should not be requested to perform \
ECDH. Creating anyway as it can be converted to Montgomery X25519"
ECDH. Creating anyway."
);
let mut rng = self.rng.lock().expect("RNG lock poisoned");
create_ed25519_key_pair(&mut *rng, private_key_uid, public_key_uid)
create_ed25519_key_pair(private_key_uid, public_key_uid)
}
#[cfg(feature = "fips")]
// Ed25519 not allowed for ECDH.
// see NIST.SP.800-186 - Section 3.1.2 table 2.
RecommendedCurve::CURVEED25519 => {
kms_not_supported!(
"An Edwards Keypair on curve 25519 should not be requested to perform \
ECDH in FIPS mode."
)
}
other => kms_not_supported!(
"Generation of Key Pair for curve: {other:?}, is not supported"
@ -400,8 +422,7 @@ impl KMS {
}
}
CryptographicAlgorithm::Ed25519 => {
let mut rng = self.rng.lock().expect("RNG lock poisoned");
create_ed25519_key_pair(&mut *rng, private_key_uid, public_key_uid)
create_ed25519_key_pair(private_key_uid, public_key_uid)
}
CryptographicAlgorithm::CoverCrypt => {
cosmian_kms_utils::crypto::cover_crypt::master_keys::create_master_keypair(

View file

@ -56,18 +56,6 @@ impl KMS {
/// - the server server certificate cannot be read
pub fn get_server_x509_certificate(&self) -> KResult<Option<Vec<u8>>> {
match &self.params.http_params {
crate::config::HttpParams::Certbot(certbot) => {
let cert = certbot.lock().expect("can't lock certificate mutex");
let (_, certificate) = cert.get_cert()?;
Ok(Some(
certificate
.first()
.ok_or(KmsError::Certificate(
"No leaf certificate in the KMS certificate chain".to_owned(),
))?
.to_pem()?,
))
}
crate::config::HttpParams::Https(p12) => {
let pem = p12
.cert
@ -83,20 +71,6 @@ impl KMS {
}
}
/// Get the enclave public key
///
/// # Errors
/// Returns a `KResult` with a `Error` if the enclave public key file cannot be read
pub fn get_sgx_enclave_public_key(&self) -> KResult<String> {
if let Some(sgx_public_signer_key) = &self.params.tee_params.sgx_public_signer_key {
return Ok(fs::read_to_string(sgx_public_signer_key)?)
}
Err(KmsError::ItemNotFound(
"No SGX signer public key".to_owned(),
))
}
/// Adds a new encrypted `SQLite` database to the KMS server.
///
/// # Returns
@ -148,23 +122,6 @@ impl KMS {
));
}
/// Return the enclave quote
///
/// This service is not available if the server is not running inside an enclave
#[cfg(target_os = "linux")]
pub fn get_attestation_report(&self, nonce: [u8; 32]) -> KResult<String> {
let certificate = self.get_server_x509_certificate()?;
if let Some(certificate) = certificate {
let report_data = cosmian_kms_utils::tee::forge_report_data(&nonce, &certificate)?;
return Ok(b64.encode(tee_attestation::get_quote(&report_data)?))
}
Err(KmsError::NotSupported(
"Can't get a report attestation without a configured certificate".to_string(),
))
}
/// This operation requests the server to Import a Managed Object specified
/// by its Unique Identifier. The request specifies the object being
/// imported and all the attributes to be assigned to the object. The
@ -571,7 +528,7 @@ impl KMS {
let uid = access
.unique_identifier
.as_ref()
.ok_or_else(|| KmsError::UnsupportedPlaceholder)?
.ok_or(KmsError::UnsupportedPlaceholder)?
.as_str()
.context("unique_identifier is not a string")?;

View file

@ -1,4 +1,3 @@
pub(crate) mod certbot;
pub(crate) mod certificate;
pub(crate) mod cover_crypt;
pub(crate) mod implementation;

View file

@ -96,11 +96,10 @@ pub async fn wrap_key(
};
// wrap the key based on the encoding
let mut rng = kms.rng.lock().expect("could not acquire a lock on the rng");
match encoding {
EncodingOption::TTLVEncoding => {
let plaintext = serde_json::to_vec(&object_key_block.key_value)?;
let ciphertext = encrypt_bytes(&mut *rng, &wrapping_key.object, &plaintext)?;
let ciphertext = encrypt_bytes(&wrapping_key.object, &plaintext)?;
object_key_block.key_value = KeyValue {
key_material: KeyMaterial::ByteString(ciphertext),
// not clear whether this should be filled or not
@ -109,7 +108,7 @@ pub async fn wrap_key(
}
EncodingOption::NoEncoding => {
let plaintext = object_key_block.key_bytes()?;
let ciphertext = encrypt_bytes(&mut *rng, &wrapping_key.object, &plaintext)?;
let ciphertext = encrypt_bytes(&wrapping_key.object, &plaintext)?;
object_key_block.key_value.key_material = KeyMaterial::ByteString(ciphertext);
}
};

View file

@ -60,10 +60,6 @@ pub enum KmsError {
#[error("Unexpected server error: {0}")]
ServerError(String),
// Any errors related to a bad behavior of the server concerning the SGX environment
#[error(transparent)]
TeeAttestationError(#[from] tee_attestation::error::Error),
// Any actions of the user which is not allowed
#[error("Access denied: {0}")]
Unauthorized(String),
@ -155,12 +151,6 @@ impl From<openssl::error::ErrorStack> for KmsError {
}
}
impl From<acme_lib::Error> for KmsError {
fn from(e: acme_lib::Error) -> Self {
Self::ServerError(e.to_string())
}
}
impl From<serde_json::Error> for KmsError {
fn from(e: serde_json::Error) -> Self {
Self::InvalidRequest(e.to_string())

View file

@ -1,17 +1,10 @@
use std::{
sync::{
atomic::{AtomicBool, Ordering},
mpsc, Arc, Mutex, RwLock,
},
time::Duration,
};
use std::sync::{mpsc, Arc, RwLock};
use actix_cors::Cors;
use actix_identity::IdentityMiddleware;
use actix_web::{
dev::ServerHandle,
middleware::Condition,
rt::{spawn, time::sleep},
web::{self, Data, JsonConfig, PayloadConfig},
App, HttpServer,
};
@ -19,12 +12,11 @@ use openssl::{
ssl::{SslAcceptor, SslAcceptorBuilder, SslMethod, SslVerifyMode},
x509::store::X509StoreBuilder,
};
use tee_attestation::is_running_inside_tee;
use tracing::{debug, error, info};
use tracing::info;
use crate::{
config::{self, HttpParams, ServerParams},
core::{certbot::Certbot, KMS},
config::{self, ServerParams},
core::KMS,
kms_bail, kms_error,
middlewares::{
ssl_auth::{extract_peer_certificate, SslAuth},
@ -43,7 +35,6 @@ use crate::{
/// The server is started using one of three methods:
/// 1. Plain HTTP,
/// 2. HTTPS with PKCS#12,
/// 3. HTTPS with certbot.
///
/// The method used depends on the server settings specified in the `ServerParams` instance provided.
///
@ -62,9 +53,6 @@ pub async fn start_kms_server(
// Log the server configuration
info!("KMS Server configuration: {:#?}", server_params);
match &server_params.http_params {
config::HttpParams::Certbot(_) => {
start_certbot_https_kms_server(server_params, kms_server_handle_tx).await
}
config::HttpParams::Https(_) => {
start_https_kms_server(server_params, kms_server_handle_tx).await
}
@ -147,7 +135,7 @@ async fn start_https_kms_server(
}
}
if let Some(verify_cert) = &server_params.verify_cert {
if let Some(verify_cert) = &server_params.client_cert {
// This line sets the mode to verify peer (client) certificates
builder.set_verify(SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT);
let mut store_builder = X509StoreBuilder::new()?;
@ -170,195 +158,6 @@ async fn start_https_kms_server(
server.await.map_err(Into::into)
}
/// Start and https server with the ability to renew its certificates
async fn start_auto_renew_https(
server_params: ServerParams,
certbot: &Arc<Mutex<Certbot>>,
server_handle_transmitter: Option<mpsc::Sender<ServerHandle>>,
) -> KResult<()> {
let kms_server = Arc::new(KMSServer::instantiate(server_params).await?);
// The loop is designed to restart the server in case it stops.
// It stops when we renew the certificates
loop {
// Define an HTTPS server
let (pk, x509) = certbot
.lock()
.expect("can't lock certificate mutex")
.get_cert()?;
debug!("Building the HTTPS server... ");
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls())?;
builder.set_private_key(&pk)?;
builder.set_certificate(&x509[0])?;
for x in x509 {
builder.add_extra_chain_cert(x)?;
}
let server = prepare_kms_server(kms_server.clone(), Some(builder)).await?;
// send the server handle to the caller
if let Some(tx) = &server_handle_transmitter {
tx.send(server.handle())?;
}
let restart = Arc::new(AtomicBool::new(false));
let restart_me = Arc::clone(&restart);
let srv = server.handle();
let cert_copy = Arc::clone(certbot);
// Define and start the thread renewing the certificate
spawn(async move {
let days_before_renew = cert_copy
.lock()
.expect("can't lock certificate mutex")
.get_days_before_renew();
let renew_in = match days_before_renew {
Ok(x) => x,
Err(error) => {
error!("Error when asking for renewing the certificate {error}");
0 // force the renew
}
};
// Wait for the renew date.
if renew_in > 0 {
info!("Waiting {renew_in} days before renewing the certificate!");
sleep(Duration::from_secs(renew_in as u64 * 3600 * 24)).await;
}
// It's time to renew!!
info!("Updating certificate now...");
let request_cert = cert_copy
.lock()
.expect("can't lock certificate mutex")
.request_cert();
match request_cert {
Ok(()) => restart_me.store(true, Ordering::Relaxed),
Err(error) => {
error!("Error when renewing the certificate {error}");
restart_me.store(false, Ordering::Relaxed);
}
}
info!("Stopping the HTTPS server...");
// Stop the HTTPS server. We don't need it anymore
srv.stop(true).await;
});
// Run server until stopped (either by ctrl-c or stopped by the previous thread)
info!("Starting the HTTPS KMS server...");
server.await?;
// We reach that part of the code when the thread renewing the certificates stops.
if restart.load(Ordering::Relaxed) {
restart.store(false, Ordering::Relaxed);
} else {
// If we reach that point, we don't want to restart.
// Contact the administrator
error!("Can't restart the HTTPS server (no valid certificate)...");
kms_bail!("Can't restart the HTTPS server (no valid certificate)...")
// Note: we could decide another behavior such as:
// Let the server up. Then the web browser or the wget will raise a security error the user can ignore
// That way, we don't stop our service.
}
}
}
async fn start_certbot_https_kms_server(
server_params: ServerParams,
server_handle_transmitter: Option<mpsc::Sender<ServerHandle>>,
) -> KResult<()> {
// Before starting any servers, check the status of our SSL certificates
let certbot = match &server_params.http_params {
HttpParams::Certbot(certbot) => certbot.clone(),
_ => kms_bail!("trying to start a TLS server but certbot is not used !"),
};
debug!("Initializing certbot");
// Recover the previous certificate if exist
certbot
.lock()
.expect("can't lock certificate mutex")
.init()?;
debug!("Checking certificates...");
let mut has_valid_cert = certbot
.lock()
.expect("can't lock certificate mutex")
.check();
let http_root_path = certbot
.lock()
.expect("can't lock certificate mutex")
.http_root_path
.clone();
if !has_valid_cert {
info!("No valid certificate found!");
info!("Starting certification process...");
// Start a HTTP server, to negotiate a certificate
let server = HttpServer::new(move || {
App::new().service(actix_files::Files::new("/", &http_root_path).use_hidden_files())
})
.workers(1)
.bind(("0.0.0.0", 80))?
.run();
// The server is not started yet here!
let succeed = Arc::new(AtomicBool::new(false));
let succeed_me = Arc::clone(&succeed);
let srv = server.handle();
let cert_copy = Arc::clone(&certbot);
spawn(async move {
// Generate the certificate in another thread
info!("Requesting acme...");
let request_cert = cert_copy
.lock()
.expect("can't lock certificate mutex")
.request_cert();
match request_cert {
Ok(()) => succeed_me.store(true, Ordering::Relaxed),
Err(error) => {
error!("Error when generating the certificate: {error}");
succeed_me.store(false, Ordering::Relaxed);
}
}
// Stop the HTTP server. We don't need it anymore
srv.stop(true).await;
});
// Run server until stopped (either by ctrl-c or stopped by the previous thread)
info!("Starting the HTTP KMS server...");
server.await?;
// Note: cert_copy is a ref to cert. So `cert.certificates` contains the new certificates
// Therefore, we do not need to call `cert.init()`. That way, we avoid several acme useless queries
has_valid_cert = succeed.load(Ordering::Relaxed)
&& certbot
.lock()
.expect("can't lock certificate mutex")
.check();
info!("Stop the HTTP server");
}
if has_valid_cert {
// Use it and start SSL Server
info!("Certificate is valid");
start_auto_renew_https(server_params, &certbot, server_handle_transmitter).await?;
} else {
error!("Abort program, failed to get a valid certificate");
kms_bail!("Abort program, failed to get a valid certificate")
}
Ok(())
}
/**
* This function prepares a server for the application. It creates an `HttpServer` instance,
* configures the routes for the application, and sets the request timeout. The server can be
@ -403,8 +202,8 @@ pub async fn prepare_kms_server(
(false, None)
};
// Determine if Client Cert Auth should be used for authentication.
let use_cert_auth = kms_server.params.verify_cert.is_some();
// Determine if the application is running inside an enclave.
let use_cert_auth = kms_server.params.client_cert.is_some();
// Determine if the application is using an encrypted SQLite database.
let is_using_sqlite_enc = matches!(
kms_server.params.db_params,
@ -479,14 +278,6 @@ pub async fn prepare_kms_server(
} else {
default_scope
};
// The default scope is extended with the /tee endpoints if the application is running inside an enclave.
let default_scope = if is_running_inside_tee() {
default_scope
.service(routes::tee::get_enclave_public_key)
.service(routes::tee::get_attestation_report)
} else {
default_scope
};
app.service(default_scope)
})

View file

@ -2,7 +2,6 @@
//! since it is declared, all the modules in other Files
//! will be resolved against the lib. So everything is exported
pub mod bootstrap_server;
pub mod config;
pub mod core;
pub mod database;
@ -12,30 +11,7 @@ pub mod middlewares;
pub mod result;
pub mod routes;
use std::{pin::Pin, sync::mpsc};
use actix_web::dev::ServerHandle;
use bootstrap_server::start_kms_server_using_bootstrap_server;
use config::ServerParams;
pub use database::KMSServer;
use futures::Future;
use kms_server::start_kms_server;
use result::KResult;
#[cfg(test)]
mod tests;
#[must_use]
pub fn start_server(
server_params: ServerParams,
kms_server_handle_tx: Option<mpsc::Sender<ServerHandle>>,
) -> Pin<Box<dyn Future<Output = KResult<()>>>> {
if server_params.bootstrap_server_params.use_bootstrap_server {
Box::pin(start_kms_server_using_bootstrap_server(
server_params,
kms_server_handle_tx,
))
} else {
Box::pin(start_kms_server(server_params, kms_server_handle_tx))
}
}

View file

@ -1,7 +1,7 @@
use cosmian_kms_server::{
config::{ClapConfig, ServerParams},
kms_server::start_kms_server,
result::KResult,
start_server,
};
use dotenvy::dotenv;
use tracing::debug;
@ -18,9 +18,6 @@ use clap::Parser;
///
/// This function sets up the necessary environment variables and logging options,
/// then parses the command line arguments using [`ClapConfig::parse()`](https://docs.rs/clap/latest/clap/struct.ClapConfig.html#method.parse).
///
/// After that, it starts the correct server based on
/// whether the bootstrap server should be used or not (using `start_bootstrap_server()` or `start_kms_server()`, respectively).
#[actix_web::main]
async fn main() -> KResult<()> {
// Set up environment variables and logging options
@ -56,12 +53,12 @@ async fn main() -> KResult<()> {
{
warn!("This is a demo version, the server will stop in 3 months");
let demo = actix_rt::spawn(expiry::demo_timeout());
futures::future::select(start_server(server_params, None), demo).await;
futures::future::select(Box::pin(start_kms_server(server_params, None)), demo).await;
}
// Start the KMS
#[cfg(not(feature = "timeout"))]
start_server(server_params, None).await?;
Box::pin(start_kms_server(server_params, None)).await?;
Ok(())
}

View file

@ -14,7 +14,6 @@ use crate::{database::KMSServer, error::KmsError, result::KResult};
pub mod access;
pub mod google_cse;
pub mod kmip;
pub mod tee;
impl actix_web::error::ResponseError for KmsError {
fn error_response(&self) -> HttpResponse {
@ -39,7 +38,6 @@ impl actix_web::error::ResponseError for KmsError {
KmsError::Unauthorized(_) => StatusCode::UNAUTHORIZED,
KmsError::DatabaseError(_)
| KmsError::TeeAttestationError(_)
| KmsError::ConversionError(_)
| KmsError::CryptographicError(_)
| KmsError::Redis(_)

View file

@ -1,64 +0,0 @@
use std::sync::Arc;
use actix_web::{
get,
web::{Data, Json},
HttpRequest,
};
use tracing::info;
use crate::{database::KMSServer, result::KResult, routes::KmsError};
/// Get the quote of the server running inside a TEE
///
/// This service is only enabled when the server is running inside a TEE (SEV or SGX)
#[cfg(target_os = "linux")]
#[get("/tee/attestation_report")]
pub async fn get_attestation_report(
req: HttpRequest,
kms: Data<Arc<KMSServer>>,
) -> KResult<Json<String>> {
use actix_web::web::Query;
use base64::{engine::general_purpose::STANDARD as b64, Engine as _};
use cosmian_kms_utils::tee::QuoteParams;
let params = Query::<QuoteParams>::from_query(req.query_string())?;
info!("GET /tee/attestation_report {}", kms.get_user(req)?);
Ok(Json(
kms.get_attestation_report(
b64.decode(&params.nonce)
.map_err(|_| {
KmsError::InvalidRequest(
"The nonce should be a valid hexadecimal value".to_string(),
)
})?
.try_into()
.map_err(|e| {
KmsError::InvalidRequest(format!("The nonce should be 32 bytes long: {e:?}"))
})?,
)?,
))
}
#[cfg(not(target_os = "linux"))]
#[get("/tee/attestation_report")]
pub async fn get_attestation_report(
_req: HttpRequest,
_kms: Data<Arc<KMSServer>>,
) -> KResult<Json<String>> {
Err(KmsError::InvalidRequest(
"Can't get the attestation report from a non-Linux KMS".to_string(),
))
}
/// Get the public key of the enclave
///
/// This service is only enabled when the server is running SGX
#[get("/tee/sgx_enclave_public_key")]
pub async fn get_enclave_public_key(
req: HttpRequest,
kms: Data<Arc<KMSServer>>,
) -> KResult<Json<String>> {
info!("GET /tee/sgx_enclave_public_key {}", kms.get_user(req)?);
Ok(Json(kms.get_sgx_enclave_public_key()?))
}

View file

@ -12,7 +12,7 @@ use cosmian_kmip::kmip::{
};
use cosmian_kms_utils::crypto::curve_25519::{
kmip_requests::{ec_create_key_pair_request, get_private_key_request, get_public_key_request},
operation::{self, to_curve_25519_256_public_key},
operation::{self, to_ec_public_key},
};
use cosmian_logger::log_utils::log_init;
@ -159,7 +159,7 @@ async fn test_curve_25519_key_pair() -> KResult<()> {
// test import of public key
let pk_bytes = pk.key_block()?.key_bytes()?;
assert_eq!(pk_bytes.len(), X25519_PUBLIC_KEY_LENGTH);
let pk = to_curve_25519_256_public_key(&pk_bytes, sk_uid);
let pk = to_ec_public_key(&pk_bytes, sk_uid, RecommendedCurve::CURVE25519);
let request = Import {
unique_identifier: UniqueIdentifier::TextString(String::new()),
object_type: ObjectType::PublicKey,
@ -264,11 +264,21 @@ async fn test_curve_25519_multiple() -> KResult<()> {
panic!("not a create key pair response payload");
};
// Should fail in fips mode since ed25519 for ECDH is not allowed.
#[cfg(feature = "fips")]
assert_eq!(
response.items[1].result_status,
ResultStatusEnumeration::OperationFailed
);
#[cfg(not(feature = "fips"))]
assert_eq!(
response.items[1].result_status,
ResultStatusEnumeration::Success
);
let Some(Operation::CreateKeyPairResponse(_)) = &response.items[1].response_payload else {
#[cfg(not(feature = "fips"))]
let Some(Operation::CreateKeyPairResponse(_)) = &response.items[1].response_payload
else {
panic!("not a create key pair response payload");
};
@ -289,11 +299,21 @@ async fn test_curve_25519_multiple() -> KResult<()> {
)
);
// Should fail in fips mode since ed25519 for ECDH is not allowed.
#[cfg(feature = "fips")]
assert_eq!(
response.items[3].result_status,
ResultStatusEnumeration::OperationFailed
);
#[cfg(not(feature = "fips"))]
assert_eq!(
response.items[3].result_status,
ResultStatusEnumeration::Success
);
let Some(Operation::CreateKeyPairResponse(_)) = &response.items[3].response_payload else {
#[cfg(not(feature = "fips"))]
let Some(Operation::CreateKeyPairResponse(_)) = &response.items[3].response_payload
else {
panic!("not a create key pair response payload");
};

View file

@ -12,7 +12,7 @@ use cosmian_kmip::kmip::{
};
use cosmian_kms_utils::crypto::curve_25519::{
kmip_requests::{ec_create_key_pair_request, get_private_key_request, get_public_key_request},
operation::{to_curve_25519_256_public_key, Q_LENGTH_BITS},
operation::{to_ec_public_key, Q_LENGTH_BITS},
};
use cosmian_logger::log_utils::log_init;
use tracing::trace;
@ -153,7 +153,7 @@ async fn test_curve_25519_key_pair() -> KResult<()> {
// test import of public key
let pk_bytes = pk_key_block.key_bytes()?;
assert_eq!(pk_bytes.len(), X25519_PUBLIC_KEY_LENGTH);
let pk = to_curve_25519_256_public_key(&pk_bytes, sk_uid);
let pk = to_ec_public_key(&pk_bytes, sk_uid, RecommendedCurve::CURVE25519);
let request = Import {
unique_identifier: UniqueIdentifier::TextString(String::new()),
object_type: ObjectType::PublicKey,

Some files were not shown because too many files have changed in this diff Show more