feat: upgrade Covercrypt to v15 (#382)
* feat: upgrade Covercrypt to v15 * fix: update MSK after USK creation * test: fix bulk tests * test: fix test_rekey_prune * test: fix not exportable sensitive cc keys * fix: support pyo3 * fix: locate tests - revert changes * fix: cargo deny * fix: rename Policy by AccessStructure * fix: rename Policy by AccessStructure * test: re-enable cli attributes handling * test: re-enable clippy on cli * chore: remove pyo3 support * chore: upgrade crypto_core to v10.0.1 * chore: upgrade cover_crypt to last commit * fix: PR review * fix: Review of the Covercrypt integration (#385) * wip * wip: review rekey and master keypair creation * remove the policy from the attributes * fix typo * fix `clippy` lints * fix formatting * ci: use rust toolchain version from arg * fix: clippy lints for new toolchain * fix: clippy lints for new toolchain --------- Co-authored-by: Manuthor <manu.coste@gmail.com> * fix: create single function to retrieve id from clap args * docs: review doc and remove dead code (#388) * review doc and remove dead code * fix build * fix clippy lints * fix fmt --------- Co-authored-by: phochard <pauline.hochard@cosmian.com> Co-authored-by: Théophile BRÉZOT <theophile.brezot@cosmian.com>
This commit is contained in:
parent
5614124311
commit
5a826b465d
184 changed files with 2100 additions and 5800 deletions
2
.github/scripts/cargo_build.sh
vendored
2
.github/scripts/cargo_build.sh
vendored
|
@ -69,7 +69,7 @@ if [ "$DEBUG_OR_RELEASE" = "release" ]; then
|
|||
crates=("crate/kmip" "crate/client")
|
||||
for crate in "${crates[@]}"; do
|
||||
cd "$crate"
|
||||
PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 cargo hack test --feature-powerset --all-targets
|
||||
cargo hack test --feature-powerset --all-targets
|
||||
cd "$ROOT_FOLDER"
|
||||
done
|
||||
fi
|
||||
|
|
2
.github/workflows/build_all.yml
vendored
2
.github/workflows/build_all.yml
vendored
|
@ -10,6 +10,8 @@ on:
|
|||
debug_or_release:
|
||||
required: true
|
||||
type: string
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
jobs:
|
||||
rhel9:
|
||||
|
|
50
.github/workflows/build_docker_image.yml
vendored
50
.github/workflows/build_docker_image.yml
vendored
|
@ -77,53 +77,3 @@ jobs:
|
|||
|
||||
outputs:
|
||||
image-tag: ${{ inputs.registry-image }}:${{ steps.meta.outputs.version }}
|
||||
|
||||
python_tests:
|
||||
name: ${{ inputs.prefix }} unit python tests
|
||||
needs:
|
||||
- build-and-push-image
|
||||
uses: ./.github/workflows/python_tests.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
kms-version: ${{ needs.build-and-push-image.outputs.image-tag }}
|
||||
branch: v5.0.1
|
||||
fips: ${{ inputs.prefix }}
|
||||
|
||||
# cloudproof_kms_js:
|
||||
# name: ${{ inputs.prefix }} KMS JS tests
|
||||
# needs:
|
||||
# - build-and-push-image
|
||||
# uses: Cosmian/reusable_workflows/.github/workflows/cloudproof_kms_js.yml@develop
|
||||
# with:
|
||||
# branch: develop
|
||||
# kms-version: ${{ needs.build-and-push-image.outputs.image-tag }}
|
||||
# fips: ${{ inputs.prefix == 'FIPS' }}
|
||||
|
||||
# cloudproof_java:
|
||||
# name: ${{ inputs.prefix }} Java tests
|
||||
# needs:
|
||||
# - build-and-push-image
|
||||
# uses: Cosmian/reusable_workflows/.github/workflows/cloudproof_java_in_docker.yml@develop
|
||||
# with:
|
||||
# branch: develop
|
||||
# target: x86_64-unknown-linux-gnu
|
||||
# extension: so
|
||||
# destination: linux-x86-64
|
||||
# os: ubuntu-20.04
|
||||
# kms-version: ${{ needs.build-and-push-image.outputs.image-tag }}
|
||||
# findex-cloud-version: 0.3.1
|
||||
# copy_fresh_build: false
|
||||
# fips: ${{ inputs.prefix == 'FIPS' }}
|
||||
|
||||
# cloudproof_python:
|
||||
# name: ${{ inputs.prefix }} cloudproof_python tests
|
||||
# needs:
|
||||
# - build-and-push-image
|
||||
# uses: Cosmian/reusable_workflows/.github/workflows/cloudproof_python.yml@develop
|
||||
# with:
|
||||
# branch: v5.0.1
|
||||
# target: x86_64-unknown-linux-gnu
|
||||
# kms-version: ${{ needs.build-and-push-image.outputs.image-tag }}
|
||||
# copy_fresh_build: true
|
||||
# findex-cloud-version: 0.3.1
|
||||
# fips: ${{ inputs.prefix == 'FIPS' }}
|
||||
|
|
7
.github/workflows/clippy.yml
vendored
7
.github/workflows/clippy.yml
vendored
|
@ -38,9 +38,14 @@ jobs:
|
|||
|
||||
# Ensure all code has been formatted with rustfmt
|
||||
- name: Check formatting
|
||||
run: cargo fmt --all -- --check --color always
|
||||
run: cargo +${{ inputs.toolchain }} fmt --all -- --check --color always
|
||||
|
||||
- name: Static analysis
|
||||
run: cargo clippy --workspace --all-targets -- -D warnings
|
||||
env:
|
||||
OPENSSL_DIR: ${{ env.OPENSSL_DIR }}
|
||||
|
||||
- name: Static analysis all features
|
||||
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
env:
|
||||
OPENSSL_DIR: ${{ env.OPENSSL_DIR }}
|
||||
|
|
51
.github/workflows/main_base.yml
vendored
51
.github/workflows/main_base.yml
vendored
|
@ -40,11 +40,6 @@ jobs:
|
|||
with:
|
||||
toolchain: ${{ inputs.toolchain }}
|
||||
|
||||
pyo3:
|
||||
uses: ./.github/workflows/maturin.yml
|
||||
with:
|
||||
toolchain: ${{ inputs.toolchain }}
|
||||
|
||||
not-fips-image:
|
||||
name: Not FIPS image build and tests
|
||||
uses: ./.github/workflows/build_docker_image.yml
|
||||
|
@ -72,7 +67,6 @@ jobs:
|
|||
- cargo-audit
|
||||
- cargo-lint
|
||||
- build
|
||||
- pyo3
|
||||
runs-on: [self-hosted, not-sgx]
|
||||
container:
|
||||
image: cosmian/docker_doc_ci
|
||||
|
@ -81,7 +75,7 @@ jobs:
|
|||
|
||||
env:
|
||||
ARCHIVE_NAMES: rhel9-${{ inputs.debug_or_release }} fips_ubuntu_20_04-${{ inputs.debug_or_release }} ubuntu_24_04-${{ inputs.debug_or_release }} macos_arm-${{
|
||||
inputs.debug_or_release }} kms_python_linux kms_python_macos_intel kms_python_macos_arm kms_python_windows
|
||||
inputs.debug_or_release }}
|
||||
|
||||
steps:
|
||||
- run: rm -rf kms_* fips_* python-* windows* ubuntu* macos* rhel9* centos*
|
||||
|
@ -152,49 +146,6 @@ jobs:
|
|||
ubuntu_22_04-release/home/runner/work/kms/kms/target/x86_64-unknown-linux-gnu/debian/*.deb \
|
||||
ubuntu_24_04-release/home/runner/work/kms/kms/target/x86_64-unknown-linux-gnu/debian/*.deb
|
||||
|
||||
python_publish:
|
||||
name: python publish
|
||||
needs:
|
||||
- cargo-audit
|
||||
- cargo-lint
|
||||
- build
|
||||
- pyo3
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- archive_name: kms_python_linux
|
||||
- archive_name: kms_python_macos_intel
|
||||
- archive_name: kms_python_macos_arm
|
||||
- archive_name: kms_python_windows
|
||||
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.7
|
||||
|
||||
- name: List directory
|
||||
if: contains(runner.os, 'Linux')
|
||||
run: find .
|
||||
|
||||
- name: Install requirements
|
||||
run: |
|
||||
set -ex
|
||||
pip install twine
|
||||
mkdir -p dist
|
||||
cp ${{ matrix.archive_name }}/*.whl dist/
|
||||
|
||||
- name: Publish package to PyPi
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
skip-existing: true
|
||||
repository-url: https://upload.pypi.org/legacy/
|
||||
print-hash: true
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
|
||||
public_documentation:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
|
164
.github/workflows/maturin.yml
vendored
164
.github/workflows/maturin.yml
vendored
|
@ -1,164 +0,0 @@
|
|||
---
|
||||
name: KMS Python build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
toolchain:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
pyo3-linux:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- run: |
|
||||
rustup default ${{ inputs.toolchain }}
|
||||
rustup target add x86_64-unknown-linux-gnu
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3
|
||||
cache: pip # caching pip dependencies
|
||||
|
||||
- name: Prerequisites
|
||||
run: |
|
||||
set -x
|
||||
pip install maturin==1.2.3
|
||||
sudo apt-get install --no-install-recommends -qq libclang-dev pkg-config
|
||||
|
||||
# build openssl
|
||||
pushd .
|
||||
cd /tmp
|
||||
wget https://openssl.org/source/old/1.1.1/openssl-1.1.1v.tar.gz
|
||||
tar -xf openssl-1.1.1v.tar.gz
|
||||
cd openssl-1.1.1v
|
||||
./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl
|
||||
make -j
|
||||
sudo make install_sw
|
||||
cd ..
|
||||
rm -rf openssl-1.1.1v*
|
||||
popd
|
||||
|
||||
cd crate/pyo3/python
|
||||
pip install -r requirements.txt
|
||||
|
||||
- name: Python maturin build
|
||||
run: |
|
||||
pushd crate/pyo3
|
||||
OPENSSL_DIR=/usr/local/ssl maturin build --release --target x86_64-unknown-linux-gnu --target-dir target_python
|
||||
popd
|
||||
|
||||
- run: find target/
|
||||
|
||||
- name: Upload WHL for Linux
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kms_python_linux
|
||||
path: target/wheels/*manylinux*.whl
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload WHL for cloudproof_python tests
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: python-x86_64-unknown-linux-gnu
|
||||
path: target/wheels/*manylinux*.whl
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
|
||||
pyo3-windows:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: |
|
||||
rustup default ${{ inputs.toolchain }}
|
||||
rustup target add x86_64-pc-windows-gnu
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3
|
||||
cache: pip # caching pip dependencies
|
||||
|
||||
- name: Prerequisites
|
||||
run: |
|
||||
set -x
|
||||
pip install maturin==1.2.3
|
||||
sudo apt-get install --no-install-recommends -qq libclang-dev gcc-mingw-w64-x86-64 pkg-config
|
||||
|
||||
# build openssl
|
||||
pushd .
|
||||
cd /tmp
|
||||
wget https://openssl.org/source/old/1.1.1/openssl-1.1.1v.tar.gz
|
||||
tar -xf openssl-1.1.1v.tar.gz
|
||||
cd openssl-1.1.1v
|
||||
./Configure --cross-compile-prefix=x86_64-w64-mingw32- mingw64 --prefix=/usr/local/ssl --openssldir=/usr/local/ssl
|
||||
make -j
|
||||
sudo make install_sw
|
||||
cd ..
|
||||
rm -rf openssl-1.1.1v*
|
||||
popd
|
||||
|
||||
cd crate/pyo3/python
|
||||
pip install -r requirements.txt
|
||||
|
||||
- name: Python maturin build
|
||||
run: |
|
||||
pushd crate/pyo3
|
||||
OPENSSL_DIR=/usr/local/ssl maturin build --release --target x86_64-pc-windows-gnu --target-dir target_python
|
||||
popd
|
||||
|
||||
- name: Upload WHL for Windows
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kms_python_windows
|
||||
path: target/wheels/*win*.whl
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
|
||||
pyo3-mac:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- mac-version: macos-13
|
||||
archive-name: kms_python_macos_intel
|
||||
target: x86_64-apple-darwin
|
||||
|
||||
- mac-version: macos-14
|
||||
archive-name: kms_python_macos_arm
|
||||
target: aarch64-apple-darwin
|
||||
runs-on: ${{ matrix.mac-version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.13
|
||||
cache: pip # caching pip dependencies
|
||||
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ inputs.toolchain }}
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Prerequisites for macos
|
||||
run: |
|
||||
set -x
|
||||
|
||||
pip3 install maturin==1.2.3
|
||||
|
||||
- name: Python maturin build
|
||||
run: |
|
||||
set -x
|
||||
cd crate/pyo3
|
||||
maturin build --release --target ${{ matrix.target }} --target-dir target_python
|
||||
|
||||
- name: Upload WHL for MacOS
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.archive-name }}
|
||||
path: target/wheels/*macosx*.whl
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
71
.github/workflows/python_tests.yml
vendored
71
.github/workflows/python_tests.yml
vendored
|
@ -1,71 +0,0 @@
|
|||
---
|
||||
name: KMS Python tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
kms-version:
|
||||
required: true
|
||||
type: string
|
||||
branch:
|
||||
required: true
|
||||
type: string
|
||||
fips:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
pyo3-test-linux:
|
||||
name: ${{ inputs.fips }} KMS Python tests
|
||||
services:
|
||||
kms:
|
||||
image: ${{ inputs.kms-version }}
|
||||
env:
|
||||
COSMIAN_SERVER_URL: http://localhost:9998
|
||||
ports:
|
||||
- 9998:9998
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Docker check container
|
||||
run: |
|
||||
docker run --rm ${{ inputs.kms-version }} --help
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Cosmian/cloudproof_python
|
||||
ref: ${{ inputs.branch }}
|
||||
|
||||
- name: Install cloudproof python deps
|
||||
env:
|
||||
COVER_CRYPT_TAG: last_build
|
||||
FINDEX_TAG: last_build
|
||||
run: |
|
||||
scripts/ci_install_pyo3_builds.sh
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- run: find .
|
||||
|
||||
- name: Install KMS python
|
||||
run: |
|
||||
# Check python code
|
||||
pip install kms_python_linux/*manylinux*.whl
|
||||
pip install -r crate/pyo3/python/requirements.txt
|
||||
|
||||
- name: Test KMS python client on KMS server
|
||||
run: |
|
||||
# Check python code
|
||||
mypy crate/pyo3/python/scripts/test_kms.py
|
||||
python3 crate/pyo3/python/scripts/test_kms.py
|
||||
if [ ! "${{ inputs.fips }}" = "FIPS" ]; then
|
||||
mypy crate/pyo3/python/scripts/test_kms_covercrypt.py
|
||||
python3 crate/pyo3/python/scripts/test_kms_covercrypt.py
|
||||
fi
|
||||
|
||||
- name: Check that the lib version is the same as the server
|
||||
run: |
|
||||
cargo install cargo-get
|
||||
diff <(cargo get --entry crate/pyo3 package.version) <(cargo get --entry crate/server package.version) || (echo "Update the version in crate/pyo3/Cargo.toml"; exit
|
||||
1)
|
|
@ -116,7 +116,7 @@ repos:
|
|||
- repo: https://github.com/Cosmian/git-hooks.git
|
||||
rev: v1.0.32
|
||||
hooks:
|
||||
- id: cargo-format
|
||||
# - id: cargo-format
|
||||
# - id: dprint-toml-fix
|
||||
# - id: cargo-upgrade
|
||||
# - id: cargo-update
|
||||
|
@ -130,7 +130,7 @@ repos:
|
|||
- id: clippy-autofix-all-targets
|
||||
- id: clippy-all-targets-all-features
|
||||
- id: clippy-all-targets
|
||||
- id: cargo-format # in last du to clippy fixes
|
||||
# - id: cargo-format # in last du to clippy fixes
|
||||
- id: docker-compose-down
|
||||
|
||||
- repo: https://github.com/EmbarkStudios/cargo-deny
|
||||
|
|
373
Cargo.lock
generated
373
Cargo.lock
generated
|
@ -1011,68 +1011,6 @@ version = "0.7.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "cloudproof"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e4a805ae87855dc2676adb766401a0cb45d1e88eb7298ea6ffedc9dc8f58e7"
|
||||
dependencies = [
|
||||
"cloudproof_aesgcm",
|
||||
"cloudproof_anonymization",
|
||||
"cloudproof_cover_crypt",
|
||||
"cloudproof_ecies",
|
||||
"cloudproof_findex 6.0.2",
|
||||
"cloudproof_fpe",
|
||||
"cosmian_crypto_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudproof_aesgcm"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0f2755b9f0f157a2a8816cb88663af7741c72df12fba115ba1f98e6e7077b15"
|
||||
dependencies = [
|
||||
"cosmian_crypto_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudproof_anonymization"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b4c483a4f8ce9ebd178dfc8578b327d195ff7d44cbdab5bb171a8c8e8c7290f"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"base64 0.21.7",
|
||||
"chrono",
|
||||
"cosmian_crypto_core",
|
||||
"hex",
|
||||
"rand 0.8.5",
|
||||
"rand_distr",
|
||||
"regex",
|
||||
"sha2",
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudproof_cover_crypt"
|
||||
version = "14.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34b957408fa6185ed0a2140da5df66cd37326da478a11a6c1a9ac07650d58121"
|
||||
dependencies = [
|
||||
"cosmian_cover_crypt",
|
||||
"cosmian_crypto_core",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudproof_ecies"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d90e7789327a566109776551d69980264647d3aab290116b8b90f3bae8c53a7"
|
||||
dependencies = [
|
||||
"cosmian_crypto_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudproof_findex"
|
||||
version = "5.0.4"
|
||||
|
@ -1080,8 +1018,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "a25dad7ff37557df4cb27a20d80727d75dfa2ddcbed14f02761e8bfb1f595741"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"cosmian_crypto_core",
|
||||
"cosmian_findex 5.0.3",
|
||||
"cosmian_crypto_core 9.5.0",
|
||||
"cosmian_findex",
|
||||
"redis",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -1090,33 +1028,6 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudproof_findex"
|
||||
version = "6.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "648998ca94264039683dd60818940cb461790cd0f62b7823caaa814869d2c7f1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"cosmian_crypto_core",
|
||||
"cosmian_findex 6.0.0",
|
||||
"serde",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudproof_fpe"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7f1457fe34566c626b37275061dcc88dd71ebf06ebd3d7ee62c6b0b37f2c426"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"cosmian_fpe",
|
||||
"itertools",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coarsetime"
|
||||
version = "0.1.34"
|
||||
|
@ -1242,12 +1153,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cosmian_cover_crypt"
|
||||
version = "14.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2162c569b1b72a9b18929bb6b6effdc59ef12101276559bbb7a01c3f08056b2e"
|
||||
version = "15.0.0"
|
||||
source = "git+https://github.com/Cosmian/cover_crypt.git?branch=release/v15.0.0#018daaf2c39a64473028200a8ee0085d1bfadfdb"
|
||||
dependencies = [
|
||||
"cosmian_crypto_core",
|
||||
"pqc_kyber",
|
||||
"cosmian_crypto_core 10.0.1",
|
||||
"ml-kem",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tiny-keccak",
|
||||
|
@ -1290,6 +1200,30 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmian_crypto_core"
|
||||
version = "10.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2157c9264217f92e928ceb538b69c0b8ce04f8c1405ae4117fb4ef0ae639c9a"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes-gcm",
|
||||
"blake2",
|
||||
"chacha20",
|
||||
"chacha20poly1305",
|
||||
"crypto_box",
|
||||
"curve25519-dalek",
|
||||
"ed25519-dalek",
|
||||
"getrandom 0.2.15",
|
||||
"leb128",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
"sha2",
|
||||
"signature",
|
||||
"tiny-keccak",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmian_findex"
|
||||
version = "5.0.3"
|
||||
|
@ -1298,43 +1232,13 @@ checksum = "5cb51a6ded995165d6343c3b74d6f25191a02cbd6a2148a1403c0583a04e9e94"
|
|||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.7",
|
||||
"cosmian_crypto_core",
|
||||
"cosmian_crypto_core 9.5.0",
|
||||
"never",
|
||||
"rand 0.8.5",
|
||||
"tiny-keccak",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmian_findex"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ac33fe1817637e96a87727995829d536c7d4554421d3ff2809cc39997bc1f91"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.7",
|
||||
"cosmian_crypto_core",
|
||||
"never",
|
||||
"tiny-keccak",
|
||||
"tracing",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmian_fpe"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8146536a868bcda32bab8d40da8696668beae1e8075f773478cd5663e856baf1"
|
||||
dependencies = [
|
||||
"cbc",
|
||||
"cipher",
|
||||
"libm",
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmian_http_client"
|
||||
version = "0.1.0"
|
||||
|
@ -1364,7 +1268,6 @@ dependencies = [
|
|||
"hex",
|
||||
"leb128",
|
||||
"num-bigint-dig",
|
||||
"pyo3",
|
||||
"rand 0.9.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -1407,8 +1310,9 @@ dependencies = [
|
|||
"assert_cmd",
|
||||
"base64 0.22.1",
|
||||
"clap",
|
||||
"cloudproof",
|
||||
"cosmian_config_utils",
|
||||
"cosmian_cover_crypt",
|
||||
"cosmian_crypto_core 10.0.1",
|
||||
"cosmian_kms_client",
|
||||
"cosmian_kms_crypto",
|
||||
"cosmian_logger",
|
||||
|
@ -1441,8 +1345,8 @@ dependencies = [
|
|||
name = "cosmian_kms_client"
|
||||
version = "4.22.1"
|
||||
dependencies = [
|
||||
"cloudproof",
|
||||
"cosmian_config_utils",
|
||||
"cosmian_crypto_core 10.0.1",
|
||||
"cosmian_http_client",
|
||||
"cosmian_kmip",
|
||||
"cosmian_kms_access",
|
||||
|
@ -1465,13 +1369,13 @@ dependencies = [
|
|||
"aes-gcm-siv",
|
||||
"argon2",
|
||||
"base64 0.22.1",
|
||||
"cloudproof",
|
||||
"cosmian_cover_crypt",
|
||||
"cosmian_crypto_core 10.0.1",
|
||||
"cosmian_kmip",
|
||||
"cosmian_logger",
|
||||
"hex",
|
||||
"num-bigint-dig",
|
||||
"openssl",
|
||||
"pyo3",
|
||||
"rust-ini",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -1494,20 +1398,6 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmian_kms_python"
|
||||
version = "4.22.1"
|
||||
dependencies = [
|
||||
"cloudproof",
|
||||
"cosmian_kmip",
|
||||
"cosmian_kms_client",
|
||||
"cosmian_kms_crypto",
|
||||
"pyo3",
|
||||
"pyo3-asyncio",
|
||||
"pyo3-build-config",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmian_kms_server"
|
||||
version = "4.22.1"
|
||||
|
@ -1524,8 +1414,9 @@ dependencies = [
|
|||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"clap",
|
||||
"cloudproof",
|
||||
"cloudproof_findex 5.0.4",
|
||||
"cloudproof_findex",
|
||||
"cosmian_cover_crypt",
|
||||
"cosmian_crypto_core 10.0.1",
|
||||
"cosmian_kmip",
|
||||
"cosmian_kms_access",
|
||||
"cosmian_kms_crypto",
|
||||
|
@ -1570,8 +1461,8 @@ version = "4.22.1"
|
|||
dependencies = [
|
||||
"async-trait",
|
||||
"clap",
|
||||
"cloudproof",
|
||||
"cloudproof_findex 5.0.4",
|
||||
"cloudproof_findex",
|
||||
"cosmian_crypto_core 10.0.1",
|
||||
"cosmian_kmip",
|
||||
"cosmian_kms_crypto",
|
||||
"cosmian_kms_interfaces",
|
||||
|
@ -2508,6 +2399,15 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hybrid-array"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2d35805454dc9f8662a98d6d61886ffe26bd465f5960e0e55345c70d5c0d2a9"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.30"
|
||||
|
@ -2525,7 +2425,7 @@ dependencies = [
|
|||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2 0.4.10",
|
||||
"socket2 0.5.8",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
|
@ -2759,12 +2659,6 @@ dependencies = [
|
|||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
|
@ -2874,6 +2768,16 @@ dependencies = [
|
|||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kem"
|
||||
version = "0.3.0-pre.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b8645470337db67b01a7f966decf7d0bafedbae74147d33e641c67a91df239f"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kms_test_server"
|
||||
version = "4.22.1"
|
||||
|
@ -3038,15 +2942,6 @@ version = "2.7.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
|
@ -3080,6 +2975,19 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ml-kem"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97befee0c869cb56f3118f49d0f9bb68c9e3f380dec23c1100aedc4ec3ba239a"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
"kem",
|
||||
"rand_core 0.6.4",
|
||||
"sha3",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.12"
|
||||
|
@ -3648,12 +3556,6 @@ dependencies = [
|
|||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
|
@ -3669,15 +3571,6 @@ dependencies = [
|
|||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pqc_kyber"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32b79004a05337e54e8ffc0ec7470e40fa26eca6fe182968ec2b803247f2283c"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "3.1.2"
|
||||
|
@ -3771,92 +3664,6 @@ dependencies = [
|
|||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53bdbb96d49157e65d45cc287af5f32ffadd5f4761438b527b055fb0d4bb8233"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"indoc",
|
||||
"libc",
|
||||
"memoffset",
|
||||
"parking_lot",
|
||||
"portable-atomic",
|
||||
"pyo3-build-config",
|
||||
"pyo3-ffi",
|
||||
"pyo3-macros",
|
||||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-asyncio"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea6b68e93db3622f3bb3bf363246cf948ed5375afe7abff98ccbdd50b184995"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"pyo3",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-build-config"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "deaa5745de3f5231ce10517a1f5dd97d53e5a2fd77aa6b5842292085831d48d7"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"python3-dll-a",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-ffi"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b42531d03e08d4ef1f6e85a2ed422eb678b8cd62b762e53891c05faf0d4afa"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pyo3-build-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7305c720fa01b8055ec95e484a6eca7a83c841267f0dd5280f0c8b8551d2c158"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-macros-backend",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros-backend"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c7e9b68bb9c3149c5b0cade5d07f953d6d125eb4337723c4ccdb665f1f96185"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"pyo3-build-config",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python3-dll-a"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd0b78171a90d808b319acfad166c4790d9e9759bbc14ac8273fe133673dd41b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
|
@ -3927,16 +3734,6 @@ dependencies = [
|
|||
"zerocopy 0.8.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_distr"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rawsql"
|
||||
version = "0.1.1"
|
||||
|
@ -4738,12 +4535,6 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "stringprep"
|
||||
version = "0.1.5"
|
||||
|
@ -4858,12 +4649,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.16.0"
|
||||
|
@ -5377,12 +5162,6 @@ version = "0.1.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.5.1"
|
||||
|
@ -5438,9 +5217,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.12.1"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
|
||||
checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
]
|
||||
|
|
|
@ -10,7 +10,6 @@ members = [
|
|||
"crate/hsm/base_hsm",
|
||||
"crate/interfaces",
|
||||
"crate/kmip",
|
||||
"crate/pyo3",
|
||||
"crate/pkcs11/module",
|
||||
"crate/pkcs11/provider",
|
||||
"crate/pkcs11/sys",
|
||||
|
@ -63,9 +62,12 @@ base64 = "0.22.1"
|
|||
bitflags = "2.8.0"
|
||||
chrono = "0.4.39"
|
||||
clap = { version = "4.5.27", default-features = false }
|
||||
cloudproof = "3.0"
|
||||
cloudproof_findex = { version = "5.0", features = ["findex-redis"] }
|
||||
cosmian_config_utils = { git = "https://www.github.com/Cosmian/http_client_server", tag = "0.1.0" }
|
||||
cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt.git", branch = "release/v15.0.0" }
|
||||
cosmian_crypto_core = { version = "10.0.1", default-features = false, features = [
|
||||
"ser",
|
||||
] }
|
||||
cosmian_http_client = { git = "https://www.github.com/Cosmian/http_client_server", tag = "0.1.0" }
|
||||
cosmian_logger = { git = "https://www.github.com/Cosmian/http_client_server", tag = "0.1.0" }
|
||||
der = { version = "0.7", default-features = false }
|
||||
|
@ -80,7 +82,6 @@ num-format = "0.4"
|
|||
num-bigint-dig = { version = "0.8", default-features = false }
|
||||
openssl = { version = "0.10.70", default-features = false }
|
||||
pem = "3.0"
|
||||
pyo3 = { version = "0.20", default-features = false }
|
||||
rand = "0.9"
|
||||
reqwest = { version = "0.11", default-features = false }
|
||||
serde = "1.0.217"
|
||||
|
@ -95,7 +96,7 @@ tokio = { version = "1.43", default-features = false }
|
|||
tracing-subscriber = { version = "0.3", default-features = false }
|
||||
tracing = "0.1"
|
||||
url = "2.5"
|
||||
uuid = "1.12.1"
|
||||
uuid = "=1.11.1"
|
||||
x509-cert = { version = "0.2", default-features = false }
|
||||
x509-parser = "0.17.0"
|
||||
zeroize = { version = "1.8", default-features = false }
|
||||
|
|
|
@ -129,7 +129,6 @@ And also some crates:
|
|||
- `kmip` which is an implementation of the KMIP standard
|
||||
- `server_database` to handle the database
|
||||
- `pkcs11_*` to handle PKCS11 support
|
||||
- `kms_pyo3` which is a KMS client in Python
|
||||
- `kms_test_server` which is a library to instantiate programmatically the KMS server.
|
||||
|
||||
**Please refer to the README of the inner directories to have more information.**
|
||||
|
|
|
@ -36,8 +36,9 @@ clap = { workspace = true, features = [
|
|||
"derive",
|
||||
"cargo",
|
||||
] }
|
||||
cloudproof = { workspace = true }
|
||||
cosmian_config_utils = { workspace = true }
|
||||
cosmian_cover_crypt = { workspace = true }
|
||||
cosmian_crypto_core = { workspace = true, features = ["ser"] }
|
||||
cosmian_kms_client = { path = "../client" }
|
||||
cosmian_kms_crypto = { path = "../crypto" }
|
||||
cosmian_logger = { workspace = true }
|
||||
|
|
|
@ -10,7 +10,10 @@ use cosmian_kms_client::{
|
|||
use tracing::trace;
|
||||
|
||||
use super::set::SetOrDeleteAttributes;
|
||||
use crate::{actions::console, cli_bail, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{console, labels::ATTRIBUTE_ID, shared::get_key_uid},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
/// Delete the KMIP object attributes.
|
||||
#[derive(Parser, Debug)]
|
||||
|
@ -62,13 +65,11 @@ impl DeleteAttributesAction {
|
|||
///
|
||||
pub async fn process(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
trace!("DeleteAttributeAction: {:?}", self);
|
||||
let id = if let Some(key_id) = &self.requested_attributes.id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.requested_attributes.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --id or one or more --tag must be specified")
|
||||
};
|
||||
let id = get_key_uid(
|
||||
self.requested_attributes.id.as_ref(),
|
||||
self.requested_attributes.tags.as_ref(),
|
||||
ATTRIBUTE_ID,
|
||||
)?;
|
||||
|
||||
for attribute in self.requested_attributes.get_attributes_from_args()? {
|
||||
self.delete_attribute(kms_rest_client, &id, Some(attribute), None)
|
||||
|
|
|
@ -12,7 +12,10 @@ use serde_json::Value;
|
|||
use strum::{EnumIter, IntoEnumIterator};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::{actions::console, cli_bail, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{console, labels::ATTRIBUTE_ID, shared::get_key_uid},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
#[derive(ValueEnum, Debug, Clone, PartialEq, Eq, EnumIter)]
|
||||
pub enum CLinkType {
|
||||
|
@ -112,7 +115,7 @@ impl From<CLinkType> for LinkType {
|
|||
pub struct GetAttributesAction {
|
||||
/// The unique identifier of the cryptographic object.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "id", short = 'i', group = "id-tags")]
|
||||
#[clap(long = ATTRIBUTE_ID, short = 'i', group = "id-tags")]
|
||||
id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -175,13 +178,7 @@ impl GetAttributesAction {
|
|||
/// - There is an error writing to the console.
|
||||
pub async fn process(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
trace!("GetAttributesAction: {:?}", self);
|
||||
let id = if let Some(key_id) = &self.id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --id or one or more --tag must be specified")
|
||||
};
|
||||
let id = get_key_uid(self.id.as_ref(), self.tags.as_ref(), ATTRIBUTE_ID)?;
|
||||
|
||||
let mut references: Vec<AttributeReference> = Vec::with_capacity(self.attribute_tags.len());
|
||||
for tag in &self.attribute_tags {
|
||||
|
|
|
@ -19,7 +19,11 @@ use tracing::{info, trace};
|
|||
use crate::{
|
||||
actions::{
|
||||
console,
|
||||
shared::utils::{build_usage_mask_from_key_usage, KeyUsage},
|
||||
labels::ATTRIBUTE_ID,
|
||||
shared::{
|
||||
get_key_uid,
|
||||
utils::{build_usage_mask_from_key_usage, KeyUsage},
|
||||
},
|
||||
},
|
||||
cli_bail,
|
||||
error::result::CliResult,
|
||||
|
@ -147,7 +151,7 @@ impl TryFrom<&VendorAttributeCli> for VendorAttribute {
|
|||
pub struct SetOrDeleteAttributes {
|
||||
/// The unique identifier of the cryptographic object.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "id", short = 'i', group = "id-tags")]
|
||||
#[clap(long = ATTRIBUTE_ID, short = 'i', group = "id-tags")]
|
||||
pub(crate) id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -348,13 +352,11 @@ impl SetAttributesAction {
|
|||
///
|
||||
pub async fn process(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
trace!("SetAttributeAction: {:?}", self);
|
||||
let id = if let Some(key_id) = &self.requested_attributes.id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.requested_attributes.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --id or one or more --tag must be specified")
|
||||
};
|
||||
let id = get_key_uid(
|
||||
self.requested_attributes.id.as_ref(),
|
||||
self.requested_attributes.tags.as_ref(),
|
||||
ATTRIBUTE_ID,
|
||||
)?;
|
||||
|
||||
let attributes_to_set = self.requested_attributes.get_attributes_from_args()?;
|
||||
if attributes_to_set.is_empty() {
|
||||
|
|
|
@ -20,7 +20,10 @@ use cosmian_kms_client::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
actions::console,
|
||||
actions::{
|
||||
console,
|
||||
labels::{CERTIFICATE_ID, CERTIFICATE_RECERTIFY},
|
||||
},
|
||||
error::{result::CliResult, CliError},
|
||||
};
|
||||
|
||||
|
@ -140,7 +143,7 @@ pub struct CertifyAction {
|
|||
/// The unique identifier of the certificate to issue or renew.
|
||||
/// If not provided, a random one will be generated when issuing a certificate,
|
||||
/// or the original one will be used when renewing a certificate.
|
||||
#[clap(long = "certificate-id", short = 'c')]
|
||||
#[clap(long = CERTIFICATE_ID, short = 'c')]
|
||||
certificate_id: Option<String>,
|
||||
|
||||
/// The path to a certificate signing request.
|
||||
|
@ -168,7 +171,7 @@ pub struct CertifyAction {
|
|||
|
||||
/// The id of a certificate to re-certify
|
||||
#[clap(
|
||||
long = "certificate-id-to-re-certify",
|
||||
long = CERTIFICATE_RECERTIFY,
|
||||
short = 'n',
|
||||
group = "csr_pk",
|
||||
required = false
|
||||
|
|
|
@ -9,9 +9,10 @@ use cosmian_kms_client::{
|
|||
use crate::{
|
||||
actions::{
|
||||
console,
|
||||
labels::KEY_ID,
|
||||
rsa::{HashFn, RsaEncryptionAlgorithm},
|
||||
shared::get_key_uid,
|
||||
},
|
||||
cli_bail,
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
|
@ -26,7 +27,7 @@ pub struct DecryptCertificateAction {
|
|||
|
||||
/// The private key unique identifier related to certificate
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
private_key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -55,13 +56,7 @@ impl DecryptCertificateAction {
|
|||
let ciphertext = read_bytes_from_file(&self.input_file)?;
|
||||
|
||||
// Recover the unique identifier or set of tags
|
||||
let id = if let Some(key_id) = &self.private_key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
let id = get_key_uid(self.private_key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
// Create the kmip query
|
||||
let decrypt_request = Decrypt {
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
use clap::Parser;
|
||||
use cosmian_kms_client::KmsClient;
|
||||
|
||||
use crate::{actions::shared::utils::destroy, cli_bail, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{
|
||||
labels::CERTIFICATE_ID,
|
||||
shared::{get_key_uid, utils::destroy},
|
||||
},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
/// Destroy a certificate.
|
||||
///
|
||||
|
@ -14,7 +20,7 @@ use crate::{actions::shared::utils::destroy, cli_bail, error::result::CliResult}
|
|||
pub struct DestroyCertificateAction {
|
||||
/// The certificate unique identifier.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "certificate-id", short = 'c', group = "certificate-tags")]
|
||||
#[clap(long = CERTIFICATE_ID, short = 'c', group = "certificate-tags")]
|
||||
certificate_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the certificate when no certificate id is specified.
|
||||
|
@ -37,14 +43,11 @@ pub struct DestroyCertificateAction {
|
|||
|
||||
impl DestroyCertificateAction {
|
||||
pub async fn run(&self, client_connector: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(certificate_id) = &self.certificate_id {
|
||||
certificate_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either `--certificate-id` or one or more `--tag` must be specified")
|
||||
};
|
||||
|
||||
let id = get_key_uid(
|
||||
self.certificate_id.as_ref(),
|
||||
self.tags.as_ref(),
|
||||
CERTIFICATE_ID,
|
||||
)?;
|
||||
destroy(client_connector, &id, self.remove).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,10 @@ use zeroize::Zeroizing;
|
|||
use crate::{
|
||||
actions::{
|
||||
console,
|
||||
labels::CERTIFICATE_ID,
|
||||
rsa::{HashFn, RsaEncryptionAlgorithm},
|
||||
shared::get_key_uid,
|
||||
},
|
||||
cli_bail,
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
|
@ -27,7 +28,7 @@ pub struct EncryptCertificateAction {
|
|||
|
||||
/// The certificate unique identifier.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "certificate-id", short = 'c', group = "key-tags")]
|
||||
#[clap(long = CERTIFICATE_ID, short = 'c', group = "key-tags")]
|
||||
certificate_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -57,15 +58,13 @@ impl EncryptCertificateAction {
|
|||
let data = Zeroizing::from(read_bytes_from_file(&self.input_file)?);
|
||||
|
||||
// Recover the unique identifier or set of tags
|
||||
let id = if let Some(key_id) = &self.certificate_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --certificate-id or one or more --tag must be specified")
|
||||
};
|
||||
let id = get_key_uid(
|
||||
self.certificate_id.as_ref(),
|
||||
self.tags.as_ref(),
|
||||
CERTIFICATE_ID,
|
||||
)?;
|
||||
|
||||
let authentication_data = self
|
||||
let authenticated_encryption_additional_data = self
|
||||
.authentication_data
|
||||
.as_ref()
|
||||
.map(|auth_data| auth_data.as_bytes().to_vec());
|
||||
|
@ -80,7 +79,7 @@ impl EncryptCertificateAction {
|
|||
let encrypt_request = Encrypt {
|
||||
unique_identifier: Some(UniqueIdentifier::TextString(id.clone())),
|
||||
data: Some(data),
|
||||
authenticated_encryption_additional_data: authentication_data,
|
||||
authenticated_encryption_additional_data,
|
||||
cryptographic_parameters,
|
||||
..Encrypt::default()
|
||||
};
|
||||
|
|
|
@ -9,7 +9,11 @@ use cosmian_kms_client::{
|
|||
};
|
||||
use tracing::log::trace;
|
||||
|
||||
use crate::{actions::console, cli_bail, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{console, labels::CERTIFICATE_ID, shared::get_key_uid},
|
||||
cli_bail,
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
#[derive(ValueEnum, Debug, Clone, PartialEq, Eq)]
|
||||
pub enum CertificateExportFormat {
|
||||
|
@ -45,12 +49,12 @@ pub struct ExportCertificateAction {
|
|||
/// The certificate unique identifier stored in the KMS; for PKCS#12, provide the private key id
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(
|
||||
long = "certificate-id",
|
||||
long = CERTIFICATE_ID,
|
||||
short = 'c',
|
||||
group = "certificate-tags",
|
||||
verbatim_doc_comment
|
||||
)]
|
||||
unique_id: Option<String>,
|
||||
certificate_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the certificate/private key when no unique id is specified.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
|
@ -88,13 +92,11 @@ impl ExportCertificateAction {
|
|||
pub async fn run(&self, client_connector: &KmsClient) -> CliResult<()> {
|
||||
trace!("Export certificate: {:?}", self);
|
||||
|
||||
let id_or_tags: String = if let Some(object_id) = &self.unique_id {
|
||||
object_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either `--certificate-id` or one or more `--tag` must be specified")
|
||||
};
|
||||
let id = get_key_uid(
|
||||
self.certificate_id.as_ref(),
|
||||
self.tags.as_ref(),
|
||||
CERTIFICATE_ID,
|
||||
)?;
|
||||
|
||||
let (key_format_type, wrapping_key_id) = match self.output_format {
|
||||
CertificateExportFormat::JsonTtlv | CertificateExportFormat::Pem => {
|
||||
|
@ -113,7 +115,7 @@ impl ExportCertificateAction {
|
|||
// export the object
|
||||
let (id, object, export_attributes) = export_object(
|
||||
client_connector,
|
||||
&id_or_tags,
|
||||
&id,
|
||||
ExportObjectParams {
|
||||
wrapping_key_id,
|
||||
allow_revoked: self.allow_revoked,
|
||||
|
@ -180,12 +182,12 @@ impl ExportCertificateAction {
|
|||
write_json_object_to_file(&to_ttlv(&export_attributes)?, &attributes_file)?;
|
||||
let stdout_attributes = format!(
|
||||
"The attributes of the certificate {} were exported to {:?}",
|
||||
&id_or_tags, &attributes_file
|
||||
&id, &attributes_file
|
||||
);
|
||||
stdout = format!("{stdout} - {stdout_attributes}");
|
||||
}
|
||||
let mut stdout = console::Stdout::new(&stdout);
|
||||
stdout.set_unique_identifier(id_or_tags);
|
||||
stdout.set_unique_identifier(id);
|
||||
stdout.write()?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use clap::{Parser, ValueEnum};
|
||||
use cloudproof::reexport::crypto_core::reexport::x509_cert;
|
||||
use cosmian_kms_client::{
|
||||
cosmian_kmip::kmip_2_1::{
|
||||
kmip_objects::Object,
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
use clap::Parser;
|
||||
use cosmian_kms_client::KmsClient;
|
||||
|
||||
use crate::{actions::shared::utils::revoke, cli_bail, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{
|
||||
labels::{CERTIFICATE_ID, TAG},
|
||||
shared::{get_key_uid, utils::revoke},
|
||||
},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
/// Revoke a certificate.
|
||||
///
|
||||
|
@ -15,13 +21,13 @@ pub struct RevokeCertificateAction {
|
|||
|
||||
/// The certificate unique identifier of the certificate to revoke.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "certificate-id", short = 'c', group = "certificate-tags")]
|
||||
#[clap(long = CERTIFICATE_ID, short = 'c', group = "certificate-tags")]
|
||||
certificate_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the certificate when no certificate id is specified.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
#[clap(
|
||||
long = "tag",
|
||||
long = TAG,
|
||||
short = 't',
|
||||
value_name = "TAG",
|
||||
group = "certificate-tags"
|
||||
|
@ -31,13 +37,11 @@ pub struct RevokeCertificateAction {
|
|||
|
||||
impl RevokeCertificateAction {
|
||||
pub async fn run(&self, client_connector: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(certificate_id) = &self.certificate_id {
|
||||
certificate_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --certificate-id or one or more --tag must be specified")
|
||||
};
|
||||
let id = get_key_uid(
|
||||
self.certificate_id.as_ref(),
|
||||
self.tags.as_ref(),
|
||||
CERTIFICATE_ID,
|
||||
)?;
|
||||
revoke(client_connector, &id, &self.revocation_reason).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,10 @@ use cosmian_kms_client::{
|
|||
KmsClient,
|
||||
};
|
||||
|
||||
use crate::{actions::console, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{console, labels::CERTIFICATE_ID},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
/// Validate a certificate.
|
||||
///
|
||||
|
@ -19,8 +22,8 @@ pub struct ValidateCertificatesAction {
|
|||
#[clap(long = "certificate", short = 'v')]
|
||||
certificate: Vec<PathBuf>,
|
||||
/// One or more Unique Identifiers of Certificate Objects.
|
||||
#[clap(long = "unique-identifier", short = 'k')]
|
||||
unique_identifier: Vec<String>,
|
||||
#[clap(long = CERTIFICATE_ID, short = 'k')]
|
||||
certificate_id: Vec<String>,
|
||||
/// A Date-Time object indicating when the certificate chain needs to be
|
||||
/// valid. If omitted, the current date and time SHALL be assumed.
|
||||
#[clap(long = "validity-time", short = 't')]
|
||||
|
@ -31,7 +34,7 @@ impl ValidateCertificatesAction {
|
|||
pub async fn run(&self, client_connector: &KmsClient) -> CliResult<()> {
|
||||
let request = build_validate_certificate_request(
|
||||
&self.certificate,
|
||||
&self.unique_identifier,
|
||||
&self.certificate_id,
|
||||
self.validity_time.clone(),
|
||||
)?;
|
||||
let validity_indicator = client_connector.validate(request).await?.validity_indicator;
|
||||
|
|
314
crate/cli/src/actions/cover_crypt/access_structure.rs
Normal file
314
crate/cli/src/actions/cover_crypt/access_structure.rs
Normal file
|
@ -0,0 +1,314 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use cosmian_cover_crypt::{EncryptionHint, MasterPublicKey, QualifiedAttribute};
|
||||
use cosmian_crypto_core::bytes_ser_de::Serializable;
|
||||
use cosmian_kms_client::{
|
||||
cosmian_kmip::KmipResultHelper,
|
||||
export_object,
|
||||
kmip_2_1::{
|
||||
kmip_objects::Object,
|
||||
ttlv::{deserializer::from_ttlv, TTLV},
|
||||
},
|
||||
read_from_json_file, ExportObjectParams, KmsClient,
|
||||
};
|
||||
use cosmian_kms_crypto::{
|
||||
crypto::cover_crypt::{
|
||||
attributes::RekeyEditAction, kmip_requests::build_rekey_keypair_request,
|
||||
},
|
||||
CryptoError,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
actions::{console, labels::KEY_ID, shared::get_key_uid},
|
||||
cli_bail,
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
/// Extract, view, or edit policies of existing keys
|
||||
#[derive(Subcommand)]
|
||||
pub enum AccessStructureCommands {
|
||||
View(ViewAction),
|
||||
AddAttribute(AddQualifiedAttributeAction),
|
||||
RemoveAttribute(RemoveAttributeAction),
|
||||
DisableAttribute(DisableAttributeAction),
|
||||
RenameAttribute(RenameAttributeAction),
|
||||
}
|
||||
|
||||
impl AccessStructureCommands {
|
||||
pub async fn process(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
match self {
|
||||
Self::View(action) => action.run(kms_rest_client).await,
|
||||
Self::AddAttribute(action) => action.run(kms_rest_client).await,
|
||||
Self::RemoveAttribute(action) => action.run(kms_rest_client).await,
|
||||
Self::DisableAttribute(action) => action.run(kms_rest_client).await,
|
||||
Self::RenameAttribute(action) => action.run(kms_rest_client).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// View the access structure of an existing public or private master key.
|
||||
///
|
||||
/// - Use the `--key-id` switch to extract the access structure from a key stored in the KMS.
|
||||
/// - Use the `--key-file` switch to extract the access structure from a Key exported as TTLV.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct ViewAction {
|
||||
/// The public or private master key ID if the key is stored in the KMS
|
||||
#[clap(long = KEY_ID, short = 'i', required_unless_present = "key_file")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// If `key-id` is not provided, use `--key-file` to provide the file containing the public or private master key in TTLV format.
|
||||
#[clap(long = "key-file", short = 'f')]
|
||||
key_file: Option<PathBuf>,
|
||||
}
|
||||
impl ViewAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let object: Object = if let Some(id) = &self.key_id {
|
||||
export_object(
|
||||
kms_rest_client,
|
||||
id,
|
||||
ExportObjectParams {
|
||||
unwrap: true,
|
||||
..ExportObjectParams::default()
|
||||
},
|
||||
)
|
||||
.await?
|
||||
.1
|
||||
} else if let Some(key_file) = &self.key_file {
|
||||
let ttlv: TTLV = read_from_json_file(key_file)?;
|
||||
from_ttlv(&ttlv)?
|
||||
} else {
|
||||
cli_bail!("either a key ID or a key TTLV file must be supplied");
|
||||
};
|
||||
let mpk = MasterPublicKey::deserialize(&object.key_block()?.key_bytes()?).map_err(|e| {
|
||||
CryptoError::Kmip(format!("Failed deserializing the CoverCrypt MPK: {e}"))
|
||||
})?;
|
||||
|
||||
let stdout: String = format!("{:?}", mpk.access_structure);
|
||||
console::Stdout::new(&stdout).write()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an attribute to the access structure of an existing private master key.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct AddQualifiedAttributeAction {
|
||||
/// The name of the attribute to create.
|
||||
/// Example: `department::rnd`
|
||||
#[clap(required = true)]
|
||||
attribute: String,
|
||||
|
||||
/// Hybridize this qualified attribute.
|
||||
#[clap(required = false, long, default_value = "false")]
|
||||
hybridized: bool,
|
||||
|
||||
/// The master secret key unique identifier stored in the KMS.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
secret_key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
#[clap(long = "tag", short = 't', value_name = "TAG", group = "key-tags")]
|
||||
tags: Option<Vec<String>>,
|
||||
}
|
||||
impl AddQualifiedAttributeAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = get_key_uid(self.secret_key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
let rekey_query = build_rekey_keypair_request(
|
||||
&id,
|
||||
&RekeyEditAction::AddAttribute(vec![(
|
||||
QualifiedAttribute::try_from(self.attribute.as_str())?,
|
||||
EncryptionHint::new(self.hybridized),
|
||||
None,
|
||||
)]),
|
||||
)?;
|
||||
|
||||
// Query the KMS with your kmip data
|
||||
let rekey_response = kms_rest_client
|
||||
.rekey_keypair(rekey_query)
|
||||
.await
|
||||
.with_context(|| "failed adding an attribute to the master keys")?;
|
||||
|
||||
let stdout = format!(
|
||||
"New attribute {} was successfully added to the master secret key {} and master \
|
||||
public key {}.",
|
||||
&self.attribute,
|
||||
&rekey_response.private_key_unique_identifier,
|
||||
&rekey_response.public_key_unique_identifier,
|
||||
);
|
||||
console::Stdout::new(&stdout).write()
|
||||
}
|
||||
}
|
||||
|
||||
/// Rename an attribute in the access structure of an existing private master key.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct RenameAttributeAction {
|
||||
/// The name of the attribute to rename.
|
||||
/// Example: `department::mkg`
|
||||
#[clap(required = true)]
|
||||
attribute: String,
|
||||
|
||||
/// The new name for the attribute.
|
||||
/// Example: `marketing`
|
||||
#[clap(required = true)]
|
||||
new_name: String,
|
||||
|
||||
/// The master secret key unique identifier stored in the KMS.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
master_secret_key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
#[clap(long = "tag", short = 't', value_name = "TAG", group = "key-tags")]
|
||||
tags: Option<Vec<String>>,
|
||||
}
|
||||
impl RenameAttributeAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = get_key_uid(
|
||||
self.master_secret_key_id.as_ref(),
|
||||
self.tags.as_ref(),
|
||||
KEY_ID,
|
||||
)?;
|
||||
|
||||
// Create the kmip query
|
||||
let rekey_query = build_rekey_keypair_request(
|
||||
&id,
|
||||
&RekeyEditAction::RenameAttribute(vec![(
|
||||
QualifiedAttribute::try_from(self.attribute.as_str())?,
|
||||
self.new_name.clone(),
|
||||
)]),
|
||||
)?;
|
||||
|
||||
// Query the KMS with your kmip data
|
||||
kms_rest_client
|
||||
.rekey_keypair(rekey_query)
|
||||
.await
|
||||
.with_context(|| "failed renaming an attribute in the master keys' access structure")?;
|
||||
|
||||
let stdout = format!(
|
||||
"Attribute {} was successfully renamed to {}.",
|
||||
&self.attribute, &self.new_name
|
||||
);
|
||||
console::Stdout::new(&stdout).write()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable an attribute from the access structure of an existing private master key.
|
||||
/// Prevents the creation of new ciphertexts for this attribute while keeping the ability to decrypt existing ones.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct DisableAttributeAction {
|
||||
/// The name of the attribute to disable.
|
||||
/// Example: `department::marketing`
|
||||
#[clap(required = true)]
|
||||
attribute: String,
|
||||
|
||||
/// The master secret key unique identifier stored in the KMS.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
master_secret_key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
#[clap(long = "tag", short = 't', value_name = "TAG", group = "key-tags")]
|
||||
tags: Option<Vec<String>>,
|
||||
}
|
||||
impl DisableAttributeAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = get_key_uid(
|
||||
self.master_secret_key_id.as_ref(),
|
||||
self.tags.as_ref(),
|
||||
KEY_ID,
|
||||
)?;
|
||||
|
||||
// Create the kmip query
|
||||
let rekey_query = build_rekey_keypair_request(
|
||||
&id,
|
||||
&RekeyEditAction::DisableAttribute(vec![QualifiedAttribute::try_from(
|
||||
self.attribute.as_str(),
|
||||
)?]),
|
||||
)?;
|
||||
|
||||
// Query the KMS with your kmip data
|
||||
let rekey_response = kms_rest_client
|
||||
.rekey_keypair(rekey_query)
|
||||
.await
|
||||
.with_context(|| "failed disabling an attribute from the master keys")?;
|
||||
|
||||
let stdout = format!(
|
||||
"Attribute {} was successfully disabled from the master public key {}.",
|
||||
&self.attribute, &rekey_response.public_key_unique_identifier,
|
||||
);
|
||||
console::Stdout::new(&stdout).write()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove an attribute from the access structure of an existing private master key.
|
||||
/// Permanently removes the ability to use this attribute in both encryptions and decryptions.
|
||||
///
|
||||
/// Note that messages whose encryption access structure does not contain any other attributes
|
||||
/// belonging to the dimension of the deleted attribute will be lost.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct RemoveAttributeAction {
|
||||
/// The name of the attribute to remove.
|
||||
/// Example: `department::marketing`
|
||||
/// Note: prevents ciphertexts only targeting this qualified attribute to be decrypted.
|
||||
#[clap(required = true)]
|
||||
attribute: String,
|
||||
|
||||
/// The master secret key unique identifier stored in the KMS.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
master_secret_key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
#[clap(long = "tag", short = 't', value_name = "TAG", group = "key-tags")]
|
||||
tags: Option<Vec<String>>,
|
||||
}
|
||||
impl RemoveAttributeAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = get_key_uid(
|
||||
self.master_secret_key_id.as_ref(),
|
||||
self.tags.as_ref(),
|
||||
KEY_ID,
|
||||
)?;
|
||||
|
||||
// Create the kmip query
|
||||
let rekey_query = build_rekey_keypair_request(
|
||||
&id,
|
||||
&RekeyEditAction::DeleteAttribute(vec![QualifiedAttribute::try_from(
|
||||
self.attribute.as_str(),
|
||||
)?]),
|
||||
)?;
|
||||
|
||||
// Query the KMS with your kmip data
|
||||
let rekey_response = kms_rest_client
|
||||
.rekey_keypair(rekey_query)
|
||||
.await
|
||||
.with_context(|| "failed removing an attribute from the master keys")?;
|
||||
|
||||
let stdout = format!(
|
||||
"Attribute {} was successfully removed from the master secret key {} and master \
|
||||
public key {}.",
|
||||
&self.attribute,
|
||||
&rekey_response.private_key_unique_identifier,
|
||||
&rekey_response.public_key_unique_identifier,
|
||||
);
|
||||
console::Stdout::new(&stdout).write()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -2,14 +2,14 @@ use std::path::PathBuf;
|
|||
|
||||
use clap::Parser;
|
||||
use cosmian_kms_client::{
|
||||
cosmian_kmip::kmip_2_1::{kmip_operations::DecryptedData, kmip_types::CryptographicAlgorithm},
|
||||
cosmian_kmip::kmip_2_1::kmip_types::CryptographicAlgorithm,
|
||||
kmip_2_1::{kmip_types::CryptographicParameters, requests::decrypt_request},
|
||||
read_bytes_from_file, read_bytes_from_files_to_bulk, write_bulk_decrypted_data,
|
||||
write_single_decrypted_data, KmsClient,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
cli_bail,
|
||||
actions::{labels::KEY_ID, shared::get_key_uid},
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
|
@ -24,7 +24,7 @@ pub struct DecryptAction {
|
|||
|
||||
/// The user key unique identifier
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -61,13 +61,7 @@ impl DecryptAction {
|
|||
};
|
||||
|
||||
// Recover the unique identifier or set of tags
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either `--key-id` or one or more `--tag` must be specified")
|
||||
};
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
// Create the kmip query
|
||||
let decrypt_request = decrypt_request(
|
||||
|
@ -92,22 +86,14 @@ impl DecryptAction {
|
|||
.await
|
||||
.with_context(|| "Can't execute the query on the kms server")?;
|
||||
|
||||
let metadata_and_cleartext: DecryptedData = decrypt_response
|
||||
.data
|
||||
.context("The plain data are empty")?
|
||||
.as_slice()
|
||||
.try_into()?;
|
||||
let cleartext = decrypt_response.data.context("The plain data are empty")?;
|
||||
|
||||
// Write the decrypted files
|
||||
if cryptographic_algorithm == CryptographicAlgorithm::CoverCryptBulk {
|
||||
write_bulk_decrypted_data(
|
||||
&metadata_and_cleartext.plaintext,
|
||||
&self.input_files,
|
||||
self.output_file.as_ref(),
|
||||
)?;
|
||||
write_bulk_decrypted_data(&cleartext, &self.input_files, self.output_file.as_ref())?;
|
||||
} else {
|
||||
write_single_decrypted_data(
|
||||
&metadata_and_cleartext.plaintext,
|
||||
&cleartext,
|
||||
&self.input_files[0],
|
||||
self.output_file.as_ref(),
|
||||
)?;
|
||||
|
|
|
@ -9,7 +9,7 @@ use cosmian_kms_client::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
cli_bail,
|
||||
actions::{labels::KEY_ID, shared::get_key_uid},
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
|
@ -29,7 +29,7 @@ pub struct EncryptAction {
|
|||
|
||||
/// The public key unique identifier.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -65,13 +65,7 @@ impl EncryptAction {
|
|||
};
|
||||
|
||||
// Recover the unique identifier or set of tags
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(tags)?
|
||||
} else {
|
||||
cli_bail!("Either `--key-id` or one or more `--tag` must be specified")
|
||||
};
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
// Create the kmip query
|
||||
let encrypt_request = encrypt_request(
|
||||
|
@ -79,7 +73,6 @@ impl EncryptAction {
|
|||
Some(self.encryption_policy.to_string()),
|
||||
data,
|
||||
None,
|
||||
None,
|
||||
self.authentication_data
|
||||
.as_deref()
|
||||
.map(|s| s.as_bytes().to_vec()),
|
||||
|
|
|
@ -2,24 +2,25 @@ use std::path::PathBuf;
|
|||
|
||||
use clap::Parser;
|
||||
use cosmian_kms_client::KmsClient;
|
||||
use cosmian_kms_crypto::crypto::cover_crypt::kmip_requests::build_create_covercrypt_master_keypair_request;
|
||||
use cosmian_kms_crypto::crypto::cover_crypt::{
|
||||
access_structure::access_structure_from_json_file,
|
||||
kmip_requests::build_create_covercrypt_master_keypair_request,
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
actions::{
|
||||
console,
|
||||
cover_crypt::policy::{policy_from_binary_file, policy_from_json_file},
|
||||
},
|
||||
cli_bail,
|
||||
actions::console,
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
/// Create a new master key pair for a given policy and return the key IDs.
|
||||
/// Create a new master keypair for a given access structure and return the key
|
||||
/// IDs.
|
||||
///
|
||||
///
|
||||
/// - The master public key is used to encrypt the files and can be safely shared.
|
||||
/// - The master secret key is used to generate user decryption keys and must be kept confidential.
|
||||
///
|
||||
/// The policy specifications must be passed as a JSON in a file, for example:
|
||||
/// The access structure specifications must be passed as a JSON in a file, for example:
|
||||
/// ```json
|
||||
/// {
|
||||
/// "Security Level::<": [
|
||||
|
@ -28,35 +29,28 @@ use crate::{
|
|||
/// "Top Secret::+"
|
||||
/// ],
|
||||
/// "Department": [
|
||||
/// "R&D",
|
||||
/// "RnD",
|
||||
/// "HR",
|
||||
/// "MKG",
|
||||
/// "FIN"
|
||||
/// ]
|
||||
/// }
|
||||
/// ```
|
||||
/// These specifications create a policy where:
|
||||
/// - the policy is defined with 2 policy axes: `Security Level` and `Department`
|
||||
/// - the `Security Level` axis is hierarchical as indicated by the `::<` suffix,
|
||||
/// - the `Security Level` axis has 3 possible values: `Protected`, `Confidential`, and `Top Secret`,
|
||||
/// - the `Department` axis has 4 possible values: `R&D`, `HR`, `MKG`, and `FIN`,
|
||||
/// - all partitions which are `Top Secret` will be encrypted using post-quantum hybridized cryptography, as indicated by the `::+` suffix on the value,
|
||||
/// - all other partitions will use classic cryptography.
|
||||
/// This specification creates an access structure with:
|
||||
/// - 2 dimensions: `Security Level` and `Department`
|
||||
/// - `Security Level` as hierarchical dimension, as indicated by the `::<` suffix,
|
||||
/// - `Security Level` has 3 possible values: `Protected`, `Confidential`, and `Top Secret`,
|
||||
/// - `Department` has 4 possible values: `RnD`, `HR`, `MKG`, and `FIN`,
|
||||
/// - all encapsulations targeting `Top Secret` will be hybridized, as indicated by the `::+` suffix on the value,
|
||||
///
|
||||
/// Tags can later be used to retrieve the keys. Tags are optional.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct CreateMasterKeyPairAction {
|
||||
/// The JSON policy specifications file to use to generate the keys.
|
||||
/// The JSON access structure specifications file to use to generate the keys.
|
||||
/// See the inline doc of the `create-master-key-pair` command for details.
|
||||
#[clap(long = "policy-specifications", short = 's', group = "policy")]
|
||||
policy_specifications_file: Option<PathBuf>,
|
||||
|
||||
/// When not using policy specifications, a policy binary file can be used instead.
|
||||
/// See the `policy` command, to create this binary file from policy specifications
|
||||
/// or to extract it from existing keys.
|
||||
#[clap(long = "policy-binary", short = 'b', group = "policy")]
|
||||
policy_binary_file: Option<PathBuf>,
|
||||
#[clap(long, short = 's')]
|
||||
specification: PathBuf,
|
||||
|
||||
/// The tag to associate with the master key pair.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
|
@ -70,36 +64,25 @@ pub struct CreateMasterKeyPairAction {
|
|||
|
||||
impl CreateMasterKeyPairAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
// Parse the json policy file
|
||||
let policy = if let Some(specs_file) = &self.policy_specifications_file {
|
||||
policy_from_json_file(specs_file)?
|
||||
} else if let Some(binary_file) = &self.policy_binary_file {
|
||||
policy_from_binary_file(binary_file)?
|
||||
} else {
|
||||
cli_bail!("either a policy specifications or policy binary file must be provided");
|
||||
};
|
||||
let access_structure = access_structure_from_json_file(&self.specification)?;
|
||||
|
||||
// Create the kmip query
|
||||
let create_key_pair =
|
||||
build_create_covercrypt_master_keypair_request(&policy, &self.tags, self.sensitive)?;
|
||||
debug!("client: access_structure: {access_structure:?}");
|
||||
|
||||
// Query the KMS with your kmip data and get the key pair ids
|
||||
let create_key_pair_response = kms_rest_client
|
||||
.create_key_pair(create_key_pair)
|
||||
let res = kms_rest_client
|
||||
.create_key_pair(build_create_covercrypt_master_keypair_request(
|
||||
&access_structure,
|
||||
&self.tags,
|
||||
self.sensitive,
|
||||
)?)
|
||||
.await
|
||||
.with_context(|| "failed creating a Covercrypt Master Key Pair")?;
|
||||
|
||||
let private_key_unique_identifier = &create_key_pair_response.private_key_unique_identifier;
|
||||
let public_key_unique_identifier = &create_key_pair_response.public_key_unique_identifier;
|
||||
|
||||
let mut stdout = console::Stdout::new("The master key pair has been properly generated.");
|
||||
let mut stdout = console::Stdout::new("The master keypair has been properly generated.");
|
||||
stdout.set_tags(Some(&self.tags));
|
||||
stdout.set_key_pair_unique_identifier(
|
||||
private_key_unique_identifier,
|
||||
public_key_unique_identifier,
|
||||
&res.private_key_unique_identifier,
|
||||
&res.public_key_unique_identifier,
|
||||
);
|
||||
stdout.write()?;
|
||||
|
||||
Ok(())
|
||||
stdout.write()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,58 +1,27 @@
|
|||
use clap::Parser;
|
||||
use cloudproof::reexport::cover_crypt::abe_policy::AccessPolicy;
|
||||
use cosmian_cover_crypt::AccessPolicy;
|
||||
use cosmian_kms_client::KmsClient;
|
||||
use cosmian_kms_crypto::crypto::cover_crypt::kmip_requests::build_create_covercrypt_user_decryption_key_request;
|
||||
use cosmian_kms_crypto::crypto::cover_crypt::kmip_requests::build_create_covercrypt_usk_request;
|
||||
|
||||
use crate::{
|
||||
actions::console,
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
/// Create a new user decryption key given an access policy expressed as a boolean expression.
|
||||
///
|
||||
///
|
||||
/// The access policy is a boolean expression over the attributes of the policy axis.
|
||||
/// For example, for the policy below, the access policy expression
|
||||
///
|
||||
/// `Department::HR && Security Level::Confidential`
|
||||
///
|
||||
/// gives decryption access to all ciphertexts in the HR/Protected partition,
|
||||
/// as well as those in the HR/Protected partition since the `Security Level` axis
|
||||
/// is hierarchical.
|
||||
///
|
||||
/// A more complex access policy giving access to the 3 partitions MKG/Confidential,
|
||||
/// MKG/Protected and HR/Protected would be
|
||||
///
|
||||
/// `(Department::MKG && Security Level::Confidential) || (Department::HR && Security Level::Protected)`
|
||||
///
|
||||
/// The policy used in these examples is
|
||||
/// ```json
|
||||
/// {
|
||||
/// "Security Level::<": [
|
||||
/// "Protected",
|
||||
/// "Confidential",
|
||||
/// "Top Secret::+"
|
||||
/// ],
|
||||
/// "Department": [
|
||||
/// "R&D",
|
||||
/// "HR",
|
||||
/// "MKG",
|
||||
/// "FIN"
|
||||
/// ]
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Tags can later be used to retrieve the key. Tags are optional.
|
||||
/// Create a new user secret key for an access policy, and index it under some
|
||||
/// (optional) tags, that can later be used to retrieve the key.
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct CreateUserKeyAction {
|
||||
/// The master private key unique identifier
|
||||
/// The master secret key unique identifier
|
||||
#[clap(required = true)]
|
||||
master_private_key_id: String,
|
||||
master_secret_key_id: String,
|
||||
|
||||
/// The access policy as a boolean expression combining policy attributes.
|
||||
/// The access policy should be expressed as a boolean expression of
|
||||
/// attributes. For example (provided the corresponding attributes are
|
||||
/// defined in the MSK):
|
||||
///
|
||||
/// Example: "(`Department::HR` || `Department::MKG`) && Security `Level::Confidential`"
|
||||
/// `"(Department::HR || Department::MKG) && Security Level::Confidential"`
|
||||
#[clap(required = true)]
|
||||
access_policy: String,
|
||||
|
||||
|
@ -68,32 +37,27 @@ pub struct CreateUserKeyAction {
|
|||
|
||||
impl CreateUserKeyAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
// Verify boolean expression in self.access_policy
|
||||
AccessPolicy::from_boolean_expression(&self.access_policy)
|
||||
.with_context(|| "bad access policy syntax")?;
|
||||
// Validate the access policy: side-effect only.
|
||||
AccessPolicy::parse(&self.access_policy).with_context(|| "bad access policy syntax")?;
|
||||
|
||||
// Create the kmip query
|
||||
let create_user_key = build_create_covercrypt_user_decryption_key_request(
|
||||
let request = build_create_covercrypt_usk_request(
|
||||
&self.access_policy,
|
||||
&self.master_private_key_id,
|
||||
&self.master_secret_key_id,
|
||||
&self.tags,
|
||||
self.sensitive,
|
||||
)?;
|
||||
|
||||
// Query the KMS with your kmip data
|
||||
let create_response = kms_rest_client
|
||||
.create(create_user_key)
|
||||
let response = kms_rest_client
|
||||
.create(request)
|
||||
.await
|
||||
.with_context(|| "user decryption key creation failed")?;
|
||||
|
||||
let user_key_unique_identifier = &create_response.unique_identifier;
|
||||
let usk_uid = &response.unique_identifier;
|
||||
|
||||
let mut stdout =
|
||||
console::Stdout::new("The user decryption key pair has been properly generated.");
|
||||
stdout.set_tags(Some(&self.tags));
|
||||
stdout.set_unique_identifier(user_key_unique_identifier.to_owned());
|
||||
stdout.write()?;
|
||||
|
||||
Ok(())
|
||||
stdout.set_unique_identifier(usk_uid.to_owned());
|
||||
stdout.write()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
use clap::Parser;
|
||||
use cosmian_kms_client::KmsClient;
|
||||
|
||||
use crate::{actions::shared::utils::destroy, cli_bail, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{
|
||||
labels::KEY_ID,
|
||||
shared::{get_key_uid, utils::destroy},
|
||||
},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
/// Destroy a Covercrypt master or user decryption key.
|
||||
///
|
||||
|
@ -19,7 +25,7 @@ use crate::{actions::shared::utils::destroy, cli_bail, error::result::CliResult}
|
|||
pub struct DestroyKeyAction {
|
||||
/// The key unique identifier.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -37,14 +43,7 @@ pub struct DestroyKeyAction {
|
|||
|
||||
impl DestroyKeyAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
destroy(kms_rest_client, &id, self.remove).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,32 +5,34 @@ use cosmian_kms_crypto::crypto::cover_crypt::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
actions::console,
|
||||
cli_bail,
|
||||
actions::{console, labels::KEY_ID, shared::get_key_uid},
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
/// Rekey the master and user keys for a given access policy.
|
||||
/// Rekey the given access policy.
|
||||
///
|
||||
/// Active user decryption keys are automatically re-keyed.
|
||||
/// Revoked or destroyed user decryption keys are not re-keyed.
|
||||
/// Active USKs are automatically re-keyed. Revoked or destroyed USKs are not
|
||||
/// re-keyed.
|
||||
///
|
||||
/// User keys that have not been rekeyed will only be able to decrypt
|
||||
/// data encrypted before this operation.
|
||||
/// USKs that have not been rekeyed will only be able to decrypt data encrypted
|
||||
/// before this operation.
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct RekeyAction {
|
||||
/// The access policy to rekey.
|
||||
/// Example: `department::marketing && level::confidential`
|
||||
/// The access policy should be expressed as a boolean expression of
|
||||
/// attributes. For example (provided the corresponding attributes are
|
||||
/// defined in the MSK):
|
||||
///
|
||||
/// `"(Department::HR || Department::MKG) && Security Level::Confidential"`
|
||||
#[clap(required = true)]
|
||||
access_policy: String,
|
||||
|
||||
/// The private master key unique identifier stored in the KMS.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
secret_key_id: Option<String>,
|
||||
/// The MSK UID stored in the KMS. If not specified, tags should be
|
||||
/// specified.
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
msk_uid: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
/// Tag to use to retrieve the MSK when no key id is specified.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
#[clap(long = "tag", short = 't', value_name = "TAG", group = "key-tags")]
|
||||
tags: Option<Vec<String>>,
|
||||
|
@ -38,64 +40,54 @@ pub struct RekeyAction {
|
|||
|
||||
impl RekeyAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.secret_key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
let uid = get_key_uid(self.msk_uid.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
// Create the kmip query
|
||||
let query = build_rekey_keypair_request(
|
||||
&id,
|
||||
&RekeyEditAction::RekeyAccessPolicy(self.access_policy.clone()),
|
||||
)?;
|
||||
|
||||
// Query the KMS with your kmip data
|
||||
let response = kms_rest_client
|
||||
.rekey_keypair(query)
|
||||
let res = kms_rest_client
|
||||
.rekey_keypair(build_rekey_keypair_request(
|
||||
&uid,
|
||||
&RekeyEditAction::RekeyAccessPolicy(self.access_policy.clone()),
|
||||
)?)
|
||||
.await
|
||||
.with_context(|| "failed rekeying the master keys")?;
|
||||
|
||||
let stdout = format!(
|
||||
"The master private key {} and master public key {} were rekeyed for the access \
|
||||
policy {:?}",
|
||||
&response.private_key_unique_identifier,
|
||||
&response.public_key_unique_identifier,
|
||||
"The MSK {} and MPK {} were rekeyed for the access policy {:?}",
|
||||
&res.private_key_unique_identifier,
|
||||
&res.public_key_unique_identifier,
|
||||
&self.access_policy
|
||||
);
|
||||
|
||||
let mut stdout = console::Stdout::new(&stdout);
|
||||
stdout.set_key_pair_unique_identifier(
|
||||
response.private_key_unique_identifier,
|
||||
response.public_key_unique_identifier,
|
||||
res.private_key_unique_identifier,
|
||||
res.public_key_unique_identifier,
|
||||
);
|
||||
stdout.write()?;
|
||||
|
||||
Ok(())
|
||||
stdout.write()
|
||||
}
|
||||
}
|
||||
|
||||
/// Prune the master and user keys for a given access policy.
|
||||
/// Prune all keys linked to an MSK w.r.t an given access policy.
|
||||
///
|
||||
/// Active user decryption keys are automatically pruned.
|
||||
/// Revoked or destroyed user decryption keys are not.
|
||||
/// Active USKs are automatically pruned. Revoked or destroyed user decryption
|
||||
/// keys are not.
|
||||
///
|
||||
/// Pruned user keys will only be able to decrypt ciphertexts
|
||||
/// generated after the last rekeying.
|
||||
/// Pruned user keys can only open encapsulations generated for this access
|
||||
/// policy since the last rekeying.
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct PruneAction {
|
||||
/// The access policy to prune.
|
||||
/// Example: `department::marketing && level::confidential`
|
||||
/// The access policy should be expressed as a boolean expression of
|
||||
/// attributes. For example (provided the corresponding attributes are
|
||||
/// defined in the MSK):
|
||||
///
|
||||
/// `"(Department::HR || Department::MKG) && Security Level::Confidential"`
|
||||
#[clap(required = true)]
|
||||
access_policy: String,
|
||||
|
||||
/// The private master key unique identifier stored in the KMS.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
secret_key_id: Option<String>,
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
msk_uid: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
|
@ -105,42 +97,31 @@ pub struct PruneAction {
|
|||
|
||||
impl PruneAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.secret_key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
let uid = get_key_uid(self.msk_uid.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
// Create the kmip query
|
||||
let query = build_rekey_keypair_request(
|
||||
&id,
|
||||
let request = build_rekey_keypair_request(
|
||||
&uid,
|
||||
&RekeyEditAction::PruneAccessPolicy(self.access_policy.clone()),
|
||||
)?;
|
||||
|
||||
// Query the KMS with your kmip data
|
||||
let response = kms_rest_client
|
||||
.rekey_keypair(query)
|
||||
let res = kms_rest_client
|
||||
.rekey_keypair(request)
|
||||
.await
|
||||
.with_context(|| "failed pruning the master keys")?;
|
||||
|
||||
let stdout = format!(
|
||||
"The master private key {} and master public key {} were pruned for the access policy \
|
||||
{:?}",
|
||||
&response.private_key_unique_identifier,
|
||||
&response.public_key_unique_identifier,
|
||||
"The MSK {} and MPK {} were pruned for the access policy {:?}",
|
||||
&res.private_key_unique_identifier,
|
||||
&res.public_key_unique_identifier,
|
||||
&self.access_policy
|
||||
);
|
||||
|
||||
let mut stdout = console::Stdout::new(&stdout);
|
||||
stdout.set_tags(self.tags.as_ref());
|
||||
stdout.set_key_pair_unique_identifier(
|
||||
response.private_key_unique_identifier,
|
||||
response.public_key_unique_identifier,
|
||||
res.private_key_unique_identifier,
|
||||
res.public_key_unique_identifier,
|
||||
);
|
||||
stdout.write()?;
|
||||
|
||||
Ok(())
|
||||
stdout.write()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
use clap::Parser;
|
||||
use cosmian_kms_client::KmsClient;
|
||||
|
||||
use crate::{actions::shared::utils::revoke, cli_bail, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{
|
||||
labels::KEY_ID,
|
||||
shared::{get_key_uid, utils::revoke},
|
||||
},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
/// Revoke a Covercrypt master or user decryption key.
|
||||
///
|
||||
|
@ -24,7 +30,7 @@ pub struct RevokeKeyAction {
|
|||
|
||||
/// The key unique identifier of the key to revoke.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -35,14 +41,7 @@ pub struct RevokeKeyAction {
|
|||
|
||||
impl RevokeKeyAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
revoke(kms_rest_client, &id, &self.revocation_reason).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,16 @@ use cosmian_kms_client::KmsClient;
|
|||
|
||||
use crate::{
|
||||
actions::cover_crypt::{
|
||||
decrypt::DecryptAction, encrypt::EncryptAction, keys::KeysCommands, policy::PolicyCommands,
|
||||
access_structure::AccessStructureCommands, decrypt::DecryptAction, encrypt::EncryptAction,
|
||||
keys::KeysCommands,
|
||||
},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
pub(crate) mod access_structure;
|
||||
pub(crate) mod decrypt;
|
||||
pub(crate) mod encrypt;
|
||||
pub(crate) mod keys;
|
||||
pub(crate) mod policy;
|
||||
|
||||
/// Manage Covercrypt keys and policies. Rotate attributes. Encrypt and decrypt data.
|
||||
#[derive(Parser)]
|
||||
|
@ -19,7 +20,7 @@ pub enum CovercryptCommands {
|
|||
#[command(subcommand)]
|
||||
Keys(KeysCommands),
|
||||
#[command(subcommand)]
|
||||
Policy(PolicyCommands),
|
||||
AccessStructure(AccessStructureCommands),
|
||||
Encrypt(EncryptAction),
|
||||
Decrypt(DecryptAction),
|
||||
}
|
||||
|
@ -37,7 +38,7 @@ impl CovercryptCommands {
|
|||
///
|
||||
pub async fn process(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
match self {
|
||||
Self::Policy(command) => command.process(kms_rest_client).await?,
|
||||
Self::AccessStructure(command) => command.process(kms_rest_client).await?,
|
||||
Self::Keys(command) => command.process(kms_rest_client).await?,
|
||||
Self::Encrypt(action) => action.run(kms_rest_client).await?,
|
||||
Self::Decrypt(action) => action.run(kms_rest_client).await?,
|
||||
|
|
|
@ -1,579 +0,0 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use cloudproof::reexport::cover_crypt::abe_policy::{Attribute, EncryptionHint, Policy};
|
||||
use cosmian_kms_client::{
|
||||
cosmian_kmip::kmip_2_1::{
|
||||
kmip_objects::Object,
|
||||
ttlv::{deserializer::from_ttlv, TTLV},
|
||||
},
|
||||
export_object, read_bytes_from_file, read_from_json_file, write_json_object_to_file,
|
||||
ExportObjectParams, KmsClient,
|
||||
};
|
||||
use cosmian_kms_crypto::crypto::cover_crypt::{
|
||||
attributes::{policy_from_attributes, RekeyEditAction},
|
||||
kmip_requests::build_rekey_keypair_request,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
actions::console,
|
||||
cli_bail,
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
pub(crate) fn policy_from_binary_file(bin_filename: &impl AsRef<Path>) -> CliResult<Policy> {
|
||||
let policy_buffer = read_bytes_from_file(bin_filename)?;
|
||||
Policy::parse_and_convert(policy_buffer.as_slice()).with_context(|| {
|
||||
format!(
|
||||
"policy binary is malformed {}",
|
||||
bin_filename.as_ref().display()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn policy_from_json_file(specs_filename: &impl AsRef<Path>) -> CliResult<Policy> {
|
||||
let policy_specs: HashMap<String, Vec<String>> = read_from_json_file(&specs_filename)?;
|
||||
policy_specs.try_into().with_context(|| {
|
||||
format!(
|
||||
"JSON policy is malformed {}",
|
||||
specs_filename.as_ref().display()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract, view, or edit policies of existing keys, and create a binary policy from specifications
|
||||
#[derive(Subcommand)]
|
||||
pub enum PolicyCommands {
|
||||
View(ViewAction),
|
||||
Specs(SpecsAction),
|
||||
Binary(BinaryAction),
|
||||
Create(CreateAction),
|
||||
AddAttribute(AddAttributeAction),
|
||||
RemoveAttribute(RemoveAttributeAction),
|
||||
DisableAttribute(DisableAttributeAction),
|
||||
RenameAttribute(RenameAttributeAction),
|
||||
}
|
||||
|
||||
impl PolicyCommands {
|
||||
pub async fn process(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
match self {
|
||||
Self::View(action) => action.run(kms_rest_client).await?,
|
||||
Self::Specs(action) => action.run(kms_rest_client).await?,
|
||||
Self::Binary(action) => action.run(kms_rest_client).await?,
|
||||
Self::Create(action) => action.run()?,
|
||||
Self::AddAttribute(action) => action.run(kms_rest_client).await?,
|
||||
Self::RemoveAttribute(action) => action.run(kms_rest_client).await?,
|
||||
Self::DisableAttribute(action) => action.run(kms_rest_client).await?,
|
||||
Self::RenameAttribute(action) => action.run(kms_rest_client).await?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a policy binary file from policy specifications
|
||||
///
|
||||
/// The policy specifications must be passed as a JSON in a file, for example:
|
||||
/// ```json
|
||||
/// {
|
||||
/// "Security Level::<": [
|
||||
/// "Protected",
|
||||
/// "Confidential",
|
||||
/// "Top Secret::+"
|
||||
/// ],
|
||||
/// "Department": [
|
||||
/// "R&D",
|
||||
/// "HR",
|
||||
/// "MKG",
|
||||
/// "FIN"
|
||||
/// ]
|
||||
/// }
|
||||
/// ```
|
||||
/// These specifications create a policy where:
|
||||
/// - the policy is defined with 2 policy axes: `Security Level` and `Department`
|
||||
/// - the `Security Level` axis is hierarchical as indicated by the `::<` suffix,
|
||||
/// - the `Security Level` axis has 3 possible values: `Protected`, `Confidential`, and `Top Secret`,
|
||||
/// - the `Department` axis has 4 possible values: `R&D`, `HR`, `MKG`, and `FIN`,
|
||||
/// - all partitions which are `Top Secret` will be encrypted using post-quantum hybridized cryptography, as indicated by the `::+` suffix on the value,
|
||||
/// - all other partitions will use classic cryptography.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct CreateAction {
|
||||
/// The policy specifications filename. The policy is expressed as a JSON object
|
||||
/// describing the Policy axes. See the documentation for
|
||||
/// details.
|
||||
#[clap(
|
||||
required = false,
|
||||
long = "specifications",
|
||||
short = 's',
|
||||
default_value = "policy_specifications.json"
|
||||
)]
|
||||
policy_specifications_file: PathBuf,
|
||||
|
||||
/// The output binary policy file generated from the specifications file.
|
||||
#[clap(
|
||||
required = false,
|
||||
long = "policy",
|
||||
short = 'p',
|
||||
default_value = "policy.bin"
|
||||
)]
|
||||
policy_binary_file: PathBuf,
|
||||
}
|
||||
|
||||
impl CreateAction {
|
||||
pub fn run(&self) -> CliResult<()> {
|
||||
// Parse the json policy file
|
||||
let policy = policy_from_json_file(&self.policy_specifications_file)?;
|
||||
|
||||
// write the binary file
|
||||
write_json_object_to_file(&policy, &self.policy_binary_file)
|
||||
.with_context(|| "failed writing the policy binary file".to_owned())?;
|
||||
|
||||
let stdout = format!(
|
||||
"The binary policy file was generated in {:?}.",
|
||||
&self.policy_binary_file
|
||||
);
|
||||
console::Stdout::new(&stdout).write()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Recover the Policy from a key store in the KMS or in a TTLV file
|
||||
async fn recover_policy(
|
||||
key_id: Option<&str>,
|
||||
key_file: Option<&PathBuf>,
|
||||
unwrap: bool,
|
||||
kms_rest_client: &KmsClient,
|
||||
) -> CliResult<Policy> {
|
||||
// Recover the KMIP Object
|
||||
let object: Object = if let Some(key_id) = key_id {
|
||||
export_object(
|
||||
kms_rest_client,
|
||||
key_id,
|
||||
ExportObjectParams {
|
||||
unwrap,
|
||||
..ExportObjectParams::default()
|
||||
},
|
||||
)
|
||||
.await?
|
||||
.1
|
||||
} else if let Some(f) = key_file {
|
||||
let ttlv: TTLV = read_from_json_file(f)?;
|
||||
from_ttlv(&ttlv)?
|
||||
} else {
|
||||
cli_bail!("either a key ID or a key TTLV file must be supplied");
|
||||
};
|
||||
// Recover the policy
|
||||
policy_from_attributes(object.attributes()?)
|
||||
.with_context(|| "failed recovering the policy from the key")
|
||||
}
|
||||
|
||||
/// Extract the policy specifications from a public or private master key to a policy specifications file
|
||||
///
|
||||
/// - Use the `--key-id` switch to extract the policy from a key stored in the KMS.
|
||||
/// - Use the `--key-file` switch to extract the policy from a Key exported as TTLV.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct SpecsAction {
|
||||
/// The public or private master key ID if the key is stored in the KMS
|
||||
#[clap(long = "key-id", short = 'i', required_unless_present = "key_file")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// If `key-id` is not provided, the file containing the public or private master key in JSON TTLV format.
|
||||
#[clap(long = "key-file", short = 'f')]
|
||||
key_file: Option<PathBuf>,
|
||||
|
||||
/// The output policy specifications file.
|
||||
#[clap(
|
||||
required = false,
|
||||
long = "specifications",
|
||||
short = 's',
|
||||
default_value = "policy_specifications.json"
|
||||
)]
|
||||
policy_specs_file: PathBuf,
|
||||
}
|
||||
impl SpecsAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
// Recover the policy
|
||||
let policy = recover_policy(
|
||||
self.key_id.as_deref(),
|
||||
self.key_file.as_ref(),
|
||||
true,
|
||||
kms_rest_client,
|
||||
)
|
||||
.await?;
|
||||
let specs: HashMap<String, Vec<String>> = policy.try_into()?;
|
||||
// save the policy to the specifications file
|
||||
Ok(write_json_object_to_file(&specs, &self.policy_specs_file)?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the policy from a public or private master key to a policy binary file
|
||||
///
|
||||
/// - Use the `--key-id` switch to extract the policy from a key stored in the KMS.
|
||||
/// - Use the `--key-file` switch to extract the policy from a Key exported as TTLV.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct BinaryAction {
|
||||
/// The public or private master key ID if the key is stored in the KMS
|
||||
#[clap(long = "key-id", short = 'i', required_unless_present = "key_file")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// If `key-id` is not provided, the file containing the public or private master key in TTLV format.
|
||||
#[clap(long = "key-file", short = 'f')]
|
||||
key_file: Option<PathBuf>,
|
||||
|
||||
/// The output binary policy file.
|
||||
#[clap(
|
||||
required = false,
|
||||
long = "policy",
|
||||
short = 'p',
|
||||
default_value = "policy.bin"
|
||||
)]
|
||||
policy_binary_file: PathBuf,
|
||||
}
|
||||
impl BinaryAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
// Recover the policy
|
||||
let policy = recover_policy(
|
||||
self.key_id.as_deref(),
|
||||
self.key_file.as_ref(),
|
||||
true,
|
||||
kms_rest_client,
|
||||
)
|
||||
.await?;
|
||||
// save the policy to the binary file
|
||||
Ok(write_json_object_to_file(
|
||||
&policy,
|
||||
&self.policy_binary_file,
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
||||
/// View the policy of an existing public or private master key.
|
||||
///
|
||||
/// - Use the `--key-id` switch to extract the policy from a key stored in the KMS.
|
||||
/// - Use the `--key-file` switch to extract the policy from a Key exported as TTLV.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct ViewAction {
|
||||
/// The public or private master key ID if the key is stored in the KMS
|
||||
#[clap(long = "key-id", short = 'i', required_unless_present = "key_file")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// If `key-id` is not provided, the file containing the public or private master key in TTLV format.
|
||||
#[clap(long = "key-file", short = 'f')]
|
||||
key_file: Option<PathBuf>,
|
||||
|
||||
/// Show all the policy details rather than just the specifications
|
||||
#[clap(
|
||||
required = false,
|
||||
long = "detailed",
|
||||
short = 'd',
|
||||
default_value = "false"
|
||||
)]
|
||||
detailed: bool,
|
||||
}
|
||||
impl ViewAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
// Recover the policy
|
||||
let policy = recover_policy(
|
||||
self.key_id.as_deref(),
|
||||
self.key_file.as_ref(),
|
||||
true,
|
||||
kms_rest_client,
|
||||
)
|
||||
.await?;
|
||||
// get a pretty json and print it
|
||||
let json = if self.detailed {
|
||||
serde_json::to_string_pretty(&policy)?
|
||||
} else {
|
||||
let specs: HashMap<String, Vec<String>> = policy.try_into()?;
|
||||
serde_json::to_string_pretty(&specs)?
|
||||
};
|
||||
console::Stdout::new(&json).write()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an attribute to the policy of an existing private master key.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct AddAttributeAction {
|
||||
/// The name of the attribute to create.
|
||||
/// Example: `department::rd`
|
||||
#[clap(required = true)]
|
||||
attribute: String,
|
||||
|
||||
/// Set encryption hint for the new attribute to use hybridized keys.
|
||||
#[clap(required = false, long = "hybridized", default_value = "false")]
|
||||
hybridized: bool,
|
||||
|
||||
/// The private master key unique identifier stored in the KMS.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
secret_key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
#[clap(long = "tag", short = 't', value_name = "TAG", group = "key-tags")]
|
||||
tags: Option<Vec<String>>,
|
||||
}
|
||||
impl AddAttributeAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.secret_key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
|
||||
let attr = Attribute::try_from(self.attribute.as_str())?;
|
||||
let enc_hint = EncryptionHint::new(self.hybridized);
|
||||
|
||||
// Create the kmip query
|
||||
let rekey_query = build_rekey_keypair_request(
|
||||
&id,
|
||||
&RekeyEditAction::AddAttribute(vec![(attr, enc_hint)]),
|
||||
)?;
|
||||
|
||||
// Query the KMS with your kmip data
|
||||
let rekey_response = kms_rest_client
|
||||
.rekey_keypair(rekey_query)
|
||||
.await
|
||||
.with_context(|| "failed adding an attribute to the master keys")?;
|
||||
|
||||
let stdout = format!(
|
||||
"New attribute {} was successfully added to the master private key {} and master \
|
||||
public key {}.",
|
||||
&self.attribute,
|
||||
&rekey_response.private_key_unique_identifier,
|
||||
&rekey_response.public_key_unique_identifier,
|
||||
);
|
||||
console::Stdout::new(&stdout).write()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Rename an attribute in the policy of an existing private master key.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct RenameAttributeAction {
|
||||
/// The name of the attribute to rename.
|
||||
/// Example: `department::mkg`
|
||||
#[clap(required = true)]
|
||||
attribute: String,
|
||||
|
||||
/// The new name for the attribute.
|
||||
/// Example: `marketing`
|
||||
#[clap(required = true)]
|
||||
new_name: String,
|
||||
|
||||
/// The private master key unique identifier stored in the KMS.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
secret_key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
#[clap(long = "tag", short = 't', value_name = "TAG", group = "key-tags")]
|
||||
tags: Option<Vec<String>>,
|
||||
}
|
||||
impl RenameAttributeAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.secret_key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
|
||||
let attr = Attribute::try_from(self.attribute.as_str())?;
|
||||
|
||||
// Create the kmip query
|
||||
let rekey_query = build_rekey_keypair_request(
|
||||
&id,
|
||||
&RekeyEditAction::RenameAttribute(vec![(attr, self.new_name.clone())]),
|
||||
)?;
|
||||
|
||||
// Query the KMS with your kmip data
|
||||
kms_rest_client
|
||||
.rekey_keypair(rekey_query)
|
||||
.await
|
||||
.with_context(|| "failed renaming an attribute in the master keys' policy")?;
|
||||
|
||||
let stdout = format!(
|
||||
"Attribute {} was successfully renamed to {}.",
|
||||
&self.attribute, &self.new_name
|
||||
);
|
||||
console::Stdout::new(&stdout).write()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable an attribute from the policy of an existing private master key.
|
||||
/// Prevents the encryption of new messages for this attribute while keeping the ability to decrypt existing ciphertexts.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct DisableAttributeAction {
|
||||
/// The name of the attribute to disable.
|
||||
/// Example: `department::marketing`
|
||||
#[clap(required = true)]
|
||||
attribute: String,
|
||||
|
||||
/// The private master key unique identifier stored in the KMS.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
secret_key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
#[clap(long = "tag", short = 't', value_name = "TAG", group = "key-tags")]
|
||||
tags: Option<Vec<String>>,
|
||||
}
|
||||
impl DisableAttributeAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.secret_key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
|
||||
let attr = Attribute::try_from(self.attribute.as_str())?;
|
||||
|
||||
// Create the kmip query
|
||||
let rekey_query =
|
||||
build_rekey_keypair_request(&id, &RekeyEditAction::DisableAttribute(vec![attr]))?;
|
||||
|
||||
// Query the KMS with your kmip data
|
||||
let rekey_response = kms_rest_client
|
||||
.rekey_keypair(rekey_query)
|
||||
.await
|
||||
.with_context(|| "failed disabling an attribute from the master keys")?;
|
||||
|
||||
let stdout = format!(
|
||||
"Attribute {} was successfully disabled from the master public key {}.",
|
||||
&self.attribute, &rekey_response.public_key_unique_identifier,
|
||||
);
|
||||
console::Stdout::new(&stdout).write()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove an attribute from the policy of an existing private master key.
|
||||
/// Permanently removes the ability to use this attribute in both encryptions and decryptions.
|
||||
///
|
||||
/// Note that messages whose encryption policy does not contain any other attributes
|
||||
/// belonging to the dimension of the deleted attribute will be lost.
|
||||
#[derive(Parser)]
|
||||
#[clap(verbatim_doc_comment)]
|
||||
pub struct RemoveAttributeAction {
|
||||
/// The name of the attribute to remove.
|
||||
/// Example: `department::marketing`
|
||||
#[clap(required = true)]
|
||||
attribute: String,
|
||||
|
||||
/// The private master key unique identifier stored in the KMS.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
secret_key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
#[clap(long = "tag", short = 't', value_name = "TAG", group = "key-tags")]
|
||||
tags: Option<Vec<String>>,
|
||||
}
|
||||
impl RemoveAttributeAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.secret_key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
|
||||
let attr = Attribute::try_from(self.attribute.as_str())?;
|
||||
|
||||
// Create the kmip query
|
||||
let rekey_query =
|
||||
build_rekey_keypair_request(&id, &RekeyEditAction::RemoveAttribute(vec![attr]))?;
|
||||
|
||||
// Query the KMS with your kmip data
|
||||
let rekey_response = kms_rest_client
|
||||
.rekey_keypair(rekey_query)
|
||||
.await
|
||||
.with_context(|| "failed removing an attribute from the master keys")?;
|
||||
|
||||
let stdout = format!(
|
||||
"Attribute {} was successfully removed from the master private key {} and master \
|
||||
public key {}.",
|
||||
&self.attribute,
|
||||
&rekey_response.private_key_unique_identifier,
|
||||
&rekey_response.public_key_unique_identifier,
|
||||
);
|
||||
console::Stdout::new(&stdout).write()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::items_after_statements)]
|
||||
mod tests {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::policy_from_binary_file;
|
||||
|
||||
#[test]
|
||||
pub(crate) fn test_policy_bin_from_file() {
|
||||
//correct
|
||||
const CORRECT_FILE: &str = "../../test_data/policy.bin";
|
||||
let result = policy_from_binary_file(&PathBuf::from(CORRECT_FILE));
|
||||
assert!(result.is_ok(), "The policy should be ok");
|
||||
|
||||
//file not found
|
||||
const WRONG_FILENAME: &str = "not_exist";
|
||||
let result = policy_from_binary_file(&PathBuf::from(WRONG_FILENAME));
|
||||
assert!(
|
||||
result
|
||||
.err()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.starts_with(&format!("could not open the file {WRONG_FILENAME}"))
|
||||
);
|
||||
|
||||
// malformed json
|
||||
const MALFORMED_FILE: &str = "../../test_data/policy.bad";
|
||||
let result = policy_from_binary_file(&PathBuf::from(MALFORMED_FILE));
|
||||
assert!(
|
||||
result
|
||||
.err()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.starts_with(&format!("policy binary is malformed {MALFORMED_FILE}"))
|
||||
);
|
||||
|
||||
// duplicate policies
|
||||
const DUPLICATED_POLICIES: &str = "../../test_data/policy.bad2";
|
||||
let result = policy_from_binary_file(&PathBuf::from(DUPLICATED_POLICIES));
|
||||
assert!(
|
||||
result
|
||||
.err()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.starts_with(&format!("policy binary is malformed {DUPLICATED_POLICIES}"))
|
||||
);
|
||||
}
|
||||
}
|
|
@ -4,8 +4,7 @@ use clap::Parser;
|
|||
use cosmian_kms_client::{kmip_2_1::requests::decrypt_request, read_bytes_from_file, KmsClient};
|
||||
|
||||
use crate::{
|
||||
actions::console,
|
||||
cli_bail,
|
||||
actions::{console, labels::KEY_ID, shared::get_key_uid},
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
|
@ -20,7 +19,7 @@ pub struct DecryptAction {
|
|||
|
||||
/// The private key unique identifier
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -44,13 +43,7 @@ impl DecryptAction {
|
|||
.with_context(|| "Cannot read bytes from the file to decrypt")?;
|
||||
|
||||
// Recover the unique identifier or set of tags
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either `--key-id` or one or more `--tag` must be specified")
|
||||
};
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
// Create the kmip query
|
||||
let decrypt_request = decrypt_request(
|
||||
|
|
|
@ -4,8 +4,7 @@ use clap::Parser;
|
|||
use cosmian_kms_client::{kmip_2_1::requests::encrypt_request, read_bytes_from_file, KmsClient};
|
||||
|
||||
use crate::{
|
||||
actions::console,
|
||||
cli_bail,
|
||||
actions::{console, labels::KEY_ID, shared::get_key_uid},
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
|
@ -20,7 +19,7 @@ pub struct EncryptAction {
|
|||
|
||||
/// The public key unique identifier.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -45,13 +44,7 @@ impl EncryptAction {
|
|||
.with_context(|| "Cannot read bytes from the file to encrypt")?;
|
||||
|
||||
// Recover the unique identifier or set of tags
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either `--key-id` or one or more `--tag` must be specified")
|
||||
};
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
// Create the kmip query
|
||||
let encrypt_request = encrypt_request(
|
||||
|
@ -59,7 +52,6 @@ impl EncryptAction {
|
|||
None,
|
||||
data,
|
||||
None,
|
||||
None,
|
||||
self.authentication_data
|
||||
.as_deref()
|
||||
.map(|s| s.as_bytes().to_vec()),
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
use clap::Parser;
|
||||
use cosmian_kms_client::KmsClient;
|
||||
|
||||
use crate::{actions::shared::utils::destroy, cli_bail, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{
|
||||
labels::KEY_ID,
|
||||
shared::{get_key_uid, utils::destroy},
|
||||
},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
/// Destroy a public or private key.
|
||||
///
|
||||
|
@ -20,7 +26,7 @@ use crate::{actions::shared::utils::destroy, cli_bail, error::result::CliResult}
|
|||
pub struct DestroyKeyAction {
|
||||
/// The key unique identifier of the key to destroy
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -38,13 +44,7 @@ pub struct DestroyKeyAction {
|
|||
|
||||
impl DestroyKeyAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
destroy(kms_rest_client, &id, self.remove).await
|
||||
}
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
use clap::Parser;
|
||||
use cosmian_kms_client::KmsClient;
|
||||
|
||||
use crate::{actions::shared::utils::revoke, cli_bail, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{
|
||||
labels::KEY_ID,
|
||||
shared::{get_key_uid, utils::revoke},
|
||||
},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
/// Revoke a public or private key.
|
||||
///
|
||||
|
@ -18,7 +24,7 @@ pub struct RevokeKeyAction {
|
|||
|
||||
/// The key unique identifier of the key to revoke.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -29,14 +35,7 @@ pub struct RevokeKeyAction {
|
|||
|
||||
impl RevokeKeyAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
revoke(kms_rest_client, &id, &self.revocation_reason).await
|
||||
}
|
||||
}
|
||||
|
|
5
crate/cli/src/actions/labels.rs
Normal file
5
crate/cli/src/actions/labels.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
pub(crate) const KEY_ID: &str = "key-id";
|
||||
pub(crate) const CERTIFICATE_ID: &str = "certificate-id";
|
||||
pub(crate) const CERTIFICATE_RECERTIFY: &str = "certificate-id-to-re-certify";
|
||||
pub(crate) const ATTRIBUTE_ID: &str = "id";
|
||||
pub(crate) const TAG: &str = "tag";
|
|
@ -8,6 +8,7 @@ pub mod cover_crypt;
|
|||
pub mod elliptic_curves;
|
||||
pub mod google;
|
||||
pub mod hash;
|
||||
pub(crate) mod labels;
|
||||
pub mod login;
|
||||
pub mod logout;
|
||||
pub mod mac;
|
||||
|
|
|
@ -6,9 +6,10 @@ use cosmian_kms_client::{kmip_2_1::requests::decrypt_request, read_bytes_from_fi
|
|||
use crate::{
|
||||
actions::{
|
||||
console,
|
||||
labels::KEY_ID,
|
||||
rsa::{HashFn, RsaEncryptionAlgorithm},
|
||||
shared::get_key_uid,
|
||||
},
|
||||
cli_bail,
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
|
@ -47,7 +48,7 @@ pub struct DecryptAction {
|
|||
|
||||
/// The private key unique identifier
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -104,13 +105,7 @@ impl DecryptAction {
|
|||
.with_context(|| "Cannot read bytes from the file to decrypt")?;
|
||||
|
||||
// Recover the unique identifier or set of tags
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either `--key-id` or one or more `--tag` must be specified")
|
||||
};
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
// Create the kmip query
|
||||
let decrypt_request = decrypt_request(
|
||||
|
|
|
@ -6,9 +6,10 @@ use cosmian_kms_client::{kmip_2_1::requests::encrypt_request, read_bytes_from_fi
|
|||
use crate::{
|
||||
actions::{
|
||||
console,
|
||||
labels::KEY_ID,
|
||||
rsa::{HashFn, RsaEncryptionAlgorithm},
|
||||
shared::get_key_uid,
|
||||
},
|
||||
cli_bail,
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
|
@ -46,7 +47,7 @@ pub struct EncryptAction {
|
|||
|
||||
/// The public key unique identifier.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -97,13 +98,7 @@ impl EncryptAction {
|
|||
.with_context(|| "Cannot read bytes from the file to encrypt")?;
|
||||
|
||||
// Recover the unique identifier or set of tags
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either `--key-id` or one or more `--tag` must be specified")
|
||||
};
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
// Create the kmip query
|
||||
let encrypt_request = encrypt_request(
|
||||
|
@ -112,7 +107,6 @@ impl EncryptAction {
|
|||
data,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(
|
||||
self.encryption_algorithm
|
||||
.to_cryptographic_parameters(self.hash_fn),
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
use clap::Parser;
|
||||
use cosmian_kms_client::KmsClient;
|
||||
|
||||
use crate::{actions::shared::utils::destroy, cli_bail, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{
|
||||
labels::KEY_ID,
|
||||
shared::{get_key_uid, utils::destroy},
|
||||
},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
/// Destroy a public or private key.
|
||||
///
|
||||
|
@ -20,7 +26,7 @@ use crate::{actions::shared::utils::destroy, cli_bail, error::result::CliResult}
|
|||
pub struct DestroyKeyAction {
|
||||
/// The key unique identifier of the key to destroy
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -54,14 +60,7 @@ impl DestroyKeyAction {
|
|||
/// * The key destruction request fails.
|
||||
/// * The KMS server query fails.
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
destroy(kms_rest_client, &id, self.remove).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
use clap::Parser;
|
||||
use cosmian_kms_client::KmsClient;
|
||||
|
||||
use crate::{actions::shared::utils::revoke, cli_bail, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{
|
||||
labels::KEY_ID,
|
||||
shared::{get_key_uid, utils::revoke},
|
||||
},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
/// Revoke a public or private key.
|
||||
///
|
||||
|
@ -18,7 +24,7 @@ pub struct RevokeKeyAction {
|
|||
|
||||
/// The key unique identifier of the key to revoke.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
pub(crate) key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -48,14 +54,7 @@ impl RevokeKeyAction {
|
|||
/// * Neither `--key-id` nor `--tag` is specified.
|
||||
/// * The revocation request fails.
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
revoke(kms_rest_client, &id, &self.revocation_reason).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@ use cosmian_kms_client::{
|
|||
write_bytes_to_file, write_kmip_object_to_file, ExportObjectParams, KmsClient,
|
||||
};
|
||||
|
||||
use super::get_key_uid;
|
||||
use crate::{
|
||||
actions::console,
|
||||
cli_bail,
|
||||
actions::{console, labels::KEY_ID},
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
|
@ -85,7 +85,7 @@ pub struct ExportKeyAction {
|
|||
|
||||
/// The key unique identifier stored in the KMS.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -173,13 +173,7 @@ impl ExportKeyAction {
|
|||
/// - There is a server error while exporting the object.
|
||||
///
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id_or_tags = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
let (key_format_type, encode_to_pem) = match self.key_format {
|
||||
// For Raw: use the default format then do the local extraction of the bytes
|
||||
|
@ -237,7 +231,7 @@ impl ExportKeyAction {
|
|||
// export the object
|
||||
let (id, object, _) = export_object(
|
||||
kms_rest_client,
|
||||
&id_or_tags,
|
||||
&id,
|
||||
ExportObjectParams {
|
||||
unwrap: self.unwrap,
|
||||
wrapping_key_id: self.wrap_key_id.as_deref(),
|
||||
|
|
19
crate/cli/src/actions/shared/get_key_uid.rs
Normal file
19
crate/cli/src/actions/shared/get_key_uid.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use tracing::trace;
|
||||
|
||||
use crate::{cli_bail, error::result::CliResult};
|
||||
|
||||
pub(crate) fn get_key_uid(
|
||||
key_id: Option<&String>,
|
||||
tags: Option<&Vec<String>>,
|
||||
argument_name: &str,
|
||||
) -> CliResult<String> {
|
||||
let id = if let Some(kid) = key_id {
|
||||
kid.clone()
|
||||
} else if let Some(tags) = tags {
|
||||
serde_json::to_string(tags)?
|
||||
} else {
|
||||
cli_bail!("Either --{argument_name} or one or more --tag must be specified")
|
||||
};
|
||||
trace!("Key UID: {id}");
|
||||
Ok(id)
|
||||
}
|
|
@ -164,13 +164,10 @@ impl ImportKeyAction {
|
|||
let object_type = object.object_type();
|
||||
|
||||
// Generate the import attributes if links are specified.
|
||||
let mut import_attributes = object
|
||||
.attributes()
|
||||
.unwrap_or(&Attributes {
|
||||
cryptographic_usage_mask,
|
||||
..Default::default()
|
||||
})
|
||||
.clone();
|
||||
let mut import_attributes = object.attributes().cloned().unwrap_or_else(|_| Attributes {
|
||||
cryptographic_usage_mask,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
if let Some(issuer_certificate_id) = &self.certificate_id {
|
||||
//let attributes = import_attributes.get_or_insert(Attributes::default());
|
||||
|
|
|
@ -75,16 +75,16 @@ pub struct LocateObjectsAction {
|
|||
/// * `OpaqueObject`
|
||||
/// * `PGPKey`
|
||||
/// * `CertificateRequest`
|
||||
#[clap(long = "object-type", short = 'o',
|
||||
#[clap(long, short = 'o',
|
||||
value_parser = ObjectTypeParser,verbatim_doc_comment)]
|
||||
object_type: Option<ObjectType>,
|
||||
|
||||
/// Locate an object which has a link to this public key id.
|
||||
#[clap(long = "public-key-id", short = 'p')]
|
||||
#[clap(long, short = 'p')]
|
||||
public_key_id: Option<String>,
|
||||
|
||||
/// Locate an object which has a link to this private key id.
|
||||
#[clap(long = "private-key-id", short = 'k')]
|
||||
#[clap(long, short = 'k')]
|
||||
private_key_id: Option<String>,
|
||||
|
||||
/// Locate an object which has a link to this certificate key id.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
pub(crate) mod export_key;
|
||||
mod get_key_uid;
|
||||
pub(crate) mod import_key;
|
||||
mod locate;
|
||||
pub mod utils;
|
||||
|
@ -8,6 +9,7 @@ mod wrap_key;
|
|||
mod unwrap_key;
|
||||
|
||||
pub use export_key::{ExportKeyAction, ExportKeyFormat};
|
||||
pub(crate) use get_key_uid::get_key_uid;
|
||||
pub use import_key::ImportKeyAction;
|
||||
pub use locate::LocateObjectsAction;
|
||||
pub use unwrap_key::UnwrapKeyAction;
|
||||
|
|
|
@ -34,6 +34,8 @@ use zeroize::Zeroizing;
|
|||
use crate::{
|
||||
actions::{
|
||||
console,
|
||||
labels::KEY_ID,
|
||||
shared::get_key_uid,
|
||||
symmetric::{DataEncryptionAlgorithm, KeyEncryptionAlgorithm},
|
||||
},
|
||||
cli_bail,
|
||||
|
@ -73,7 +75,7 @@ pub struct DecryptAction {
|
|||
|
||||
/// The private key unique identifier
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -120,13 +122,7 @@ pub struct DecryptAction {
|
|||
impl DecryptAction {
|
||||
pub(crate) async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
// Recover the unique identifier or set of tags
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either `--key-id` or one or more `--tag` must be specified")
|
||||
};
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
// Write the decrypted file
|
||||
let output_file_name = self
|
||||
|
|
|
@ -23,9 +23,10 @@ use zeroize::Zeroizing;
|
|||
use crate::{
|
||||
actions::{
|
||||
console,
|
||||
labels::KEY_ID,
|
||||
shared::get_key_uid,
|
||||
symmetric::{DataEncryptionAlgorithm, KeyEncryptionAlgorithm},
|
||||
},
|
||||
cli_bail,
|
||||
error::{
|
||||
result::{CliResult, CliResultHelper},
|
||||
CliError,
|
||||
|
@ -62,7 +63,7 @@ pub struct EncryptAction {
|
|||
|
||||
/// The symmetric key unique identifier.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// The data encryption algorithm.
|
||||
|
@ -115,13 +116,7 @@ pub struct EncryptAction {
|
|||
impl EncryptAction {
|
||||
pub(crate) async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
// Recover the unique identifier or set of tags
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either `--key-id` or one or more `--tag` must be specified")
|
||||
};
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
|
||||
let nonce = self
|
||||
.nonce
|
||||
|
@ -209,7 +204,6 @@ impl EncryptAction {
|
|||
data_encryption_key_id,
|
||||
None,
|
||||
plaintext,
|
||||
None,
|
||||
nonce,
|
||||
authenticated_data,
|
||||
Some(cryptographic_parameters),
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
use clap::Parser;
|
||||
use cosmian_kms_client::KmsClient;
|
||||
|
||||
use crate::{actions::shared::utils::destroy, cli_bail, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{
|
||||
labels::KEY_ID,
|
||||
shared::{get_key_uid, utils::destroy},
|
||||
},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
/// Destroy a symmetric key.
|
||||
///
|
||||
|
@ -17,7 +23,7 @@ use crate::{actions::shared::utils::destroy, cli_bail, error::result::CliResult}
|
|||
pub struct DestroyKeyAction {
|
||||
/// The key unique identifier.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -35,14 +41,7 @@ pub struct DestroyKeyAction {
|
|||
|
||||
impl DestroyKeyAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
destroy(kms_rest_client, &id, self.remove).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use cosmian_kms_client::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
actions::console,
|
||||
actions::{console, labels::KEY_ID},
|
||||
error::result::{CliResult, CliResultHelper},
|
||||
};
|
||||
|
||||
|
@ -15,7 +15,7 @@ use crate::{
|
|||
pub struct ReKeyAction {
|
||||
/// The tag to associate with the key.
|
||||
/// To specify multiple tags, use the option multiple times.
|
||||
#[clap(long = "key-id", short = 'k')]
|
||||
#[clap(long = KEY_ID, short = 'k')]
|
||||
key_id: String,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
use clap::Parser;
|
||||
use cosmian_kms_client::KmsClient;
|
||||
|
||||
use crate::{actions::shared::utils::revoke, cli_bail, error::result::CliResult};
|
||||
use crate::{
|
||||
actions::{
|
||||
labels::KEY_ID,
|
||||
shared::{get_key_uid, utils::revoke},
|
||||
},
|
||||
error::result::CliResult,
|
||||
};
|
||||
|
||||
/// Revoke a symmetric key.
|
||||
///
|
||||
|
@ -15,7 +21,7 @@ pub struct RevokeKeyAction {
|
|||
|
||||
/// The key unique identifier of the key to revoke.
|
||||
/// If not specified, tags should be specified
|
||||
#[clap(long = "key-id", short = 'k', group = "key-tags")]
|
||||
#[clap(long = KEY_ID, short = 'k', group = "key-tags")]
|
||||
key_id: Option<String>,
|
||||
|
||||
/// Tag to use to retrieve the key when no key id is specified.
|
||||
|
@ -26,13 +32,7 @@ pub struct RevokeKeyAction {
|
|||
|
||||
impl RevokeKeyAction {
|
||||
pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
|
||||
let id = if let Some(key_id) = &self.key_id {
|
||||
key_id.clone()
|
||||
} else if let Some(tags) = &self.tags {
|
||||
serde_json::to_string(&tags)?
|
||||
} else {
|
||||
cli_bail!("Either --key-id or one or more --tag must be specified")
|
||||
};
|
||||
let id = get_key_uid(self.key_id.as_ref(), self.tags.as_ref(), KEY_ID)?;
|
||||
revoke(kms_rest_client, &id, &self.revocation_reason).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ pub enum CliError {
|
|||
Conversion(String),
|
||||
|
||||
#[error(transparent)]
|
||||
CovercryptError(#[from] cloudproof::reexport::cover_crypt::Error),
|
||||
CovercryptError(#[from] cosmian_cover_crypt::Error),
|
||||
|
||||
// A cryptographic error
|
||||
#[error("Cryptographic error: {0}")]
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
// restriction lints
|
||||
clippy::map_err_ignore,
|
||||
clippy::print_stdout,
|
||||
clippy::redundant_clone
|
||||
clippy::redundant_clone,
|
||||
clippy::renamed_function_params,
|
||||
)]
|
||||
#![allow(
|
||||
clippy::module_name_repetitions,
|
||||
|
|
|
@ -96,7 +96,7 @@ pub(crate) fn validate_certificate(
|
|||
args.push(certificate);
|
||||
}
|
||||
for uid in uids {
|
||||
args.push("--unique-identifier".to_owned());
|
||||
args.push("--certificate-id".to_owned());
|
||||
args.push(uid);
|
||||
}
|
||||
if let Some(d) = date {
|
||||
|
|
|
@ -21,13 +21,13 @@ use crate::{
|
|||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_view_policy() -> CliResult<()> {
|
||||
async fn test_view_access_structure() -> CliResult<()> {
|
||||
let ctx = start_default_test_kms_server().await;
|
||||
let mut cmd = Command::cargo_bin(PROG_NAME)?;
|
||||
cmd.env(KMS_CLI_CONF_ENV, &ctx.owner_client_conf_path);
|
||||
|
||||
cmd.arg(SUB_COMMAND).args(vec![
|
||||
"policy",
|
||||
"access-structure",
|
||||
"view",
|
||||
"-f",
|
||||
"../../test_data/ttlv_public_key.json",
|
||||
|
@ -35,55 +35,34 @@ async fn test_view_policy() -> CliResult<()> {
|
|||
recover_cmd_logs(&mut cmd);
|
||||
cmd.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("Security Level::<"))
|
||||
.stdout(predicate::str::contains("Top Secret::+"))
|
||||
.stdout(predicate::str::contains("Security Level"))
|
||||
.stdout(predicate::str::contains("Top Secret"))
|
||||
.stdout(predicate::str::contains("R&D"));
|
||||
|
||||
let mut cmd = Command::cargo_bin(PROG_NAME)?;
|
||||
cmd.env(KMS_CLI_CONF_ENV, &ctx.owner_client_conf_path);
|
||||
|
||||
cmd.arg(SUB_COMMAND).args(vec![
|
||||
"policy",
|
||||
"access-structure",
|
||||
"view",
|
||||
"-f",
|
||||
"../../test_data/ttlv_public_key.json",
|
||||
"--detailed",
|
||||
]);
|
||||
recover_cmd_logs(&mut cmd);
|
||||
cmd.assert()
|
||||
.success()
|
||||
.stdout(predicate::str::contains("\"Security Level\""))
|
||||
.stdout(predicate::str::contains("\"Top Secret\""))
|
||||
.stdout(predicate::str::contains("\"last_attribute_value\": 7"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_policy() -> CliResult<()> {
|
||||
let ctx = start_default_test_kms_server().await;
|
||||
let mut cmd = Command::cargo_bin(PROG_NAME)?;
|
||||
cmd.env(KMS_CLI_CONF_ENV, &ctx.owner_client_conf_path);
|
||||
|
||||
cmd.arg(SUB_COMMAND).args(vec![
|
||||
"policy",
|
||||
"create",
|
||||
"-s",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"-p",
|
||||
"/tmp/policy.bin",
|
||||
]);
|
||||
recover_cmd_logs(&mut cmd);
|
||||
cmd.assert().success().stdout(predicate::str::contains(
|
||||
"The binary policy file was generated in \"/tmp/policy.bin\".",
|
||||
));
|
||||
.stdout(predicate::str::contains(
|
||||
"Attribute { id: 6, encryption_hint: Classic, write_status: EncryptDecrypt }",
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn rename(
|
||||
cli_conf_path: &str,
|
||||
master_private_key_id: &str,
|
||||
master_secret_key_id: &str,
|
||||
attribute: &str,
|
||||
new_name: &str,
|
||||
) -> CliResult<()> {
|
||||
|
@ -93,10 +72,10 @@ pub(crate) async fn rename(
|
|||
cmd.env(KMS_CLI_CONF_ENV, cli_conf_path);
|
||||
|
||||
let args = vec![
|
||||
"policy",
|
||||
"access-structure",
|
||||
"rename-attribute",
|
||||
"--key-id",
|
||||
master_private_key_id,
|
||||
master_secret_key_id,
|
||||
attribute,
|
||||
new_name,
|
||||
];
|
||||
|
@ -112,7 +91,7 @@ pub(crate) async fn rename(
|
|||
|
||||
pub(crate) async fn add(
|
||||
cli_conf_path: &str,
|
||||
master_private_key_id: &str,
|
||||
master_secret_key_id: &str,
|
||||
new_attribute: &str,
|
||||
) -> CliResult<()> {
|
||||
start_default_test_kms_server().await;
|
||||
|
@ -121,10 +100,10 @@ pub(crate) async fn add(
|
|||
cmd.env(KMS_CLI_CONF_ENV, cli_conf_path);
|
||||
|
||||
let args = vec![
|
||||
"policy",
|
||||
"access-structure",
|
||||
"add-attribute",
|
||||
"--key-id",
|
||||
master_private_key_id,
|
||||
master_secret_key_id,
|
||||
new_attribute,
|
||||
];
|
||||
cmd.arg(SUB_COMMAND).args(args);
|
||||
|
@ -139,7 +118,7 @@ pub(crate) async fn add(
|
|||
|
||||
pub(crate) async fn disable(
|
||||
cli_conf_path: &str,
|
||||
master_private_key_id: &str,
|
||||
master_secret_key_id: &str,
|
||||
attribute: &str,
|
||||
) -> CliResult<()> {
|
||||
start_default_test_kms_server().await;
|
||||
|
@ -148,10 +127,10 @@ pub(crate) async fn disable(
|
|||
cmd.env(KMS_CLI_CONF_ENV, cli_conf_path);
|
||||
|
||||
let args = vec![
|
||||
"policy",
|
||||
"access-structure",
|
||||
"disable-attribute",
|
||||
"--key-id",
|
||||
master_private_key_id,
|
||||
master_secret_key_id,
|
||||
attribute,
|
||||
];
|
||||
cmd.arg(SUB_COMMAND).args(args);
|
||||
|
@ -166,7 +145,7 @@ pub(crate) async fn disable(
|
|||
|
||||
pub(crate) async fn remove(
|
||||
cli_conf_path: &str,
|
||||
master_private_key_id: &str,
|
||||
master_secret_key_id: &str,
|
||||
attribute: &str,
|
||||
) -> CliResult<()> {
|
||||
start_default_test_kms_server().await;
|
||||
|
@ -175,10 +154,10 @@ pub(crate) async fn remove(
|
|||
cmd.env(KMS_CLI_CONF_ENV, cli_conf_path);
|
||||
|
||||
let args = vec![
|
||||
"policy",
|
||||
"access-structure",
|
||||
"remove-attribute",
|
||||
"--key-id",
|
||||
master_private_key_id,
|
||||
master_secret_key_id,
|
||||
attribute,
|
||||
];
|
||||
cmd.arg(SUB_COMMAND).args(args);
|
||||
|
@ -192,7 +171,7 @@ pub(crate) async fn remove(
|
|||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_edit_policy() -> CliResult<()> {
|
||||
async fn test_edit_access_structure() -> CliResult<()> {
|
||||
let ctx = start_default_test_kms_server().await;
|
||||
// create a temp dir
|
||||
let tmp_dir = TempDir::new()?;
|
||||
|
@ -204,16 +183,16 @@ async fn test_edit_policy() -> CliResult<()> {
|
|||
let recovered_file = tmp_path.join("plain.txt");
|
||||
|
||||
// generate a new master key pair
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
let (master_secret_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
let user_decryption_key = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"(Department::MKG || Department::FIN) && Security Level::Top Secret",
|
||||
&[],
|
||||
false,
|
||||
|
@ -240,7 +219,7 @@ async fn test_edit_policy() -> CliResult<()> {
|
|||
// Rename MKG to Marketing
|
||||
rename(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"Department::MKG",
|
||||
"Marketing",
|
||||
)
|
||||
|
@ -258,7 +237,7 @@ async fn test_edit_policy() -> CliResult<()> {
|
|||
// Adding new attribute "Department::Sales"
|
||||
add(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"Department::Sales",
|
||||
)
|
||||
.await?;
|
||||
|
@ -276,7 +255,7 @@ async fn test_edit_policy() -> CliResult<()> {
|
|||
// Create a new user key with access to both the new and the renamed attribute
|
||||
let sales_mkg_user_decryption_key = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"(Department::Sales || Department::Marketing) && Security Level::Confidential",
|
||||
&[],
|
||||
false,
|
||||
|
@ -306,7 +285,7 @@ async fn test_edit_policy() -> CliResult<()> {
|
|||
// disable attribute Sales
|
||||
disable(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"Department::Sales",
|
||||
)
|
||||
.await?;
|
||||
|
@ -336,7 +315,7 @@ async fn test_edit_policy() -> CliResult<()> {
|
|||
// remove attribute Sales
|
||||
remove(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"Department::Sales",
|
||||
)
|
||||
.await?;
|
|
@ -97,10 +97,10 @@ async fn test_encrypt_decrypt_using_object_ids() -> CliResult<()> {
|
|||
fs::remove_file(&output_file).ok();
|
||||
assert!(!output_file.exists());
|
||||
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
let (master_secret_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
@ -117,7 +117,7 @@ async fn test_encrypt_decrypt_using_object_ids() -> CliResult<()> {
|
|||
// create a user decryption key
|
||||
let user_ok_key_id = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"(Department::MKG || Department::FIN) && Security Level::Top Secret",
|
||||
&[],
|
||||
false,
|
||||
|
@ -140,7 +140,7 @@ async fn test_encrypt_decrypt_using_object_ids() -> CliResult<()> {
|
|||
// this user key should not be able to decrypt the file
|
||||
let user_ko_key_id = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"Department::FIN && Security Level::Top Secret",
|
||||
&[],
|
||||
false,
|
||||
|
@ -187,10 +187,10 @@ async fn test_encrypt_decrypt_bulk_using_object_ids() -> CliResult<()> {
|
|||
fs::remove_file(&output_file3).ok();
|
||||
assert!(!output_file3.exists());
|
||||
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
let (master_secret_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
@ -215,7 +215,7 @@ async fn test_encrypt_decrypt_bulk_using_object_ids() -> CliResult<()> {
|
|||
// create a user decryption key
|
||||
let user_ok_key_id = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"(Department::MKG || Department::FIN) && Security Level::Top Secret",
|
||||
&[],
|
||||
false,
|
||||
|
@ -254,7 +254,7 @@ async fn test_encrypt_decrypt_bulk_using_object_ids() -> CliResult<()> {
|
|||
// this user key should not be able to decrypt the file
|
||||
let user_ko_key_id = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"Department::FIN && Security Level::Top Secret",
|
||||
&[],
|
||||
false,
|
||||
|
@ -307,10 +307,10 @@ async fn test_encrypt_decrypt_using_tags() -> CliResult<()> {
|
|||
fs::remove_file(&output_file).ok();
|
||||
assert!(!output_file.exists());
|
||||
|
||||
let (_master_private_key_id, _master_public_key_id) = create_cc_master_key_pair(
|
||||
let (master_secret_key_id, _master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&["tag"],
|
||||
false,
|
||||
)?;
|
||||
|
@ -327,7 +327,7 @@ async fn test_encrypt_decrypt_using_tags() -> CliResult<()> {
|
|||
// create a user decryption key
|
||||
let user_ok_key_id = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
"[\"tag\"]",
|
||||
&master_secret_key_id,
|
||||
"(Department::MKG || Department::FIN) && Security Level::Top Secret",
|
||||
&["tag"],
|
||||
false,
|
||||
|
@ -371,7 +371,7 @@ async fn test_encrypt_decrypt_using_tags() -> CliResult<()> {
|
|||
// this user key should not be able to decrypt the file
|
||||
let _user_ko_key_id = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
"[\"tag\"]",
|
||||
&master_secret_key_id,
|
||||
"Department::FIN && Security Level::Top Secret",
|
||||
&["tag_ko"],
|
||||
false,
|
||||
|
@ -430,10 +430,10 @@ async fn test_encrypt_decrypt_bulk_using_tags() -> CliResult<()> {
|
|||
fs::remove_file(&output_file3).ok();
|
||||
assert!(!output_file3.exists());
|
||||
|
||||
let (_master_private_key_id, _master_public_key_id) = create_cc_master_key_pair(
|
||||
let (master_secret_key_id, _master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&["tag_bulk"],
|
||||
false,
|
||||
)?;
|
||||
|
@ -458,7 +458,7 @@ async fn test_encrypt_decrypt_bulk_using_tags() -> CliResult<()> {
|
|||
// create a user decryption key
|
||||
let user_ok_key_id = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
"[\"tag_bulk\"]",
|
||||
&master_secret_key_id,
|
||||
"(Department::MKG || Department::FIN) && Security Level::Top Secret",
|
||||
&["tag_bulk"],
|
||||
false,
|
||||
|
|
|
@ -43,15 +43,13 @@ pub(crate) fn create_cc_master_key_pair(
|
|||
let master_keys_output = std::str::from_utf8(&output.stdout)?;
|
||||
assert!(master_keys_output.contains("Private key unique identifier: "));
|
||||
assert!(master_keys_output.contains("Public key unique identifier: "));
|
||||
let master_private_key_id = extract_private_key(master_keys_output)
|
||||
.ok_or_else(|| {
|
||||
CliError::Default("failed extracting the master private key".to_owned())
|
||||
})?
|
||||
let master_secret_key_id = extract_private_key(master_keys_output)
|
||||
.ok_or_else(|| CliError::Default("failed extracting the master secret key".to_owned()))?
|
||||
.to_owned();
|
||||
let master_public_key_id = extract_public_key(master_keys_output)
|
||||
.ok_or_else(|| CliError::Default("failed extracting the master public key".to_owned()))?
|
||||
.to_owned();
|
||||
return Ok((master_private_key_id, master_public_key_id))
|
||||
return Ok((master_secret_key_id, master_public_key_id))
|
||||
}
|
||||
|
||||
Err(CliError::Default(
|
||||
|
@ -62,53 +60,12 @@ pub(crate) fn create_cc_master_key_pair(
|
|||
#[tokio::test]
|
||||
pub(crate) async fn test_create_master_key_pair() -> CliResult<()> {
|
||||
let ctx = start_default_test_kms_server().await;
|
||||
// from specs
|
||||
create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
//from binary
|
||||
create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-binary",
|
||||
"../../test_data/policy.bin",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
pub(crate) async fn test_create_master_key_pair_error() -> CliResult<()> {
|
||||
let ctx = start_default_test_kms_server().await;
|
||||
|
||||
let err = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/notfound.json",
|
||||
&[],
|
||||
false,
|
||||
)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert!(err.to_string().contains("ERROR: could not open the file"));
|
||||
|
||||
let err = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-binary",
|
||||
"../../test_data/policy.bad",
|
||||
&[],
|
||||
false,
|
||||
)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert!(
|
||||
err.to_string()
|
||||
.contains("ERROR: policy binary is malformed")
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
pub(crate) const SUB_COMMAND: &str = "cc";
|
||||
pub(crate) mod access_structure;
|
||||
mod conf;
|
||||
mod encrypt_decrypt;
|
||||
pub(crate) mod master_key_pair;
|
||||
pub(crate) mod policy;
|
||||
mod rekey;
|
||||
pub(crate) mod user_decryption_keys;
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
use std::{path::PathBuf, process::Command};
|
||||
|
||||
use assert_cmd::prelude::*;
|
||||
use cosmian_cover_crypt::{
|
||||
api::Covercrypt, AccessPolicy, EncryptedHeader, MasterSecretKey, UserSecretKey,
|
||||
};
|
||||
use cosmian_crypto_core::bytes_ser_de::{test_serialization, Deserializer, Serializable};
|
||||
use cosmian_kms_client::KMS_CLI_CONF_ENV;
|
||||
use cosmian_kms_crypto::crypto::cover_crypt::access_structure::access_structure_from_json_file;
|
||||
use kms_test_server::start_default_test_kms_server;
|
||||
use tempfile::TempDir;
|
||||
|
||||
|
@ -22,13 +27,11 @@ use crate::{
|
|||
},
|
||||
};
|
||||
|
||||
pub(crate) async fn rekey(
|
||||
pub(crate) fn rekey(
|
||||
cli_conf_path: &str,
|
||||
master_private_key_id: &str,
|
||||
master_secret_key_id: &str,
|
||||
access_policy: &str,
|
||||
) -> CliResult<()> {
|
||||
start_default_test_kms_server().await;
|
||||
|
||||
let mut cmd = Command::cargo_bin(PROG_NAME)?;
|
||||
cmd.env(KMS_CLI_CONF_ENV, cli_conf_path);
|
||||
|
||||
|
@ -36,7 +39,7 @@ pub(crate) async fn rekey(
|
|||
"keys",
|
||||
"rekey",
|
||||
"--key-id",
|
||||
master_private_key_id,
|
||||
master_secret_key_id,
|
||||
access_policy,
|
||||
];
|
||||
cmd.arg(SUB_COMMAND).args(args);
|
||||
|
@ -49,13 +52,11 @@ pub(crate) async fn rekey(
|
|||
))
|
||||
}
|
||||
|
||||
pub(crate) async fn prune(
|
||||
pub(crate) fn prune(
|
||||
cli_conf_path: &str,
|
||||
master_private_key_id: &str,
|
||||
master_secret_key_id: &str,
|
||||
access_policy: &str,
|
||||
) -> CliResult<()> {
|
||||
start_default_test_kms_server().await;
|
||||
|
||||
let mut cmd = Command::cargo_bin(PROG_NAME)?;
|
||||
cmd.env(KMS_CLI_CONF_ENV, cli_conf_path);
|
||||
|
||||
|
@ -63,7 +64,7 @@ pub(crate) async fn prune(
|
|||
"keys",
|
||||
"prune",
|
||||
"--key-id",
|
||||
master_private_key_id,
|
||||
master_secret_key_id,
|
||||
access_policy,
|
||||
];
|
||||
cmd.arg(SUB_COMMAND).args(args);
|
||||
|
@ -81,16 +82,16 @@ async fn test_rekey_error() -> CliResult<()> {
|
|||
let ctx = start_default_test_kms_server().await;
|
||||
|
||||
// generate a new master key pair
|
||||
let (master_private_key_id, _master_public_key_id) = create_cc_master_key_pair(
|
||||
let (master_secret_key_id, _master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
let _user_decryption_key = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"(Department::MKG || Department::FIN) && Security Level::Top Secret",
|
||||
&[],
|
||||
false,
|
||||
|
@ -100,10 +101,9 @@ async fn test_rekey_error() -> CliResult<()> {
|
|||
assert!(
|
||||
rekey(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"bad_access_policy"
|
||||
)
|
||||
.await
|
||||
.is_err()
|
||||
);
|
||||
|
||||
|
@ -114,7 +114,6 @@ async fn test_rekey_error() -> CliResult<()> {
|
|||
"bad_key",
|
||||
"Department::MKG || Department::FIN"
|
||||
)
|
||||
.await
|
||||
.is_err()
|
||||
);
|
||||
|
||||
|
@ -131,7 +130,7 @@ async fn test_rekey_error() -> CliResult<()> {
|
|||
export_key(ExportKeyParams {
|
||||
cli_conf_path: ctx.owner_client_conf_path.clone(),
|
||||
sub_command: SUB_COMMAND.to_owned(),
|
||||
key_id: master_private_key_id.to_string(),
|
||||
key_id: master_secret_key_id,
|
||||
key_file: exported_wrapped_key_file.to_str().unwrap().to_string(),
|
||||
wrap_key_id: Some(symmetric_key_id),
|
||||
..Default::default()
|
||||
|
@ -153,13 +152,123 @@ async fn test_rekey_error() -> CliResult<()> {
|
|||
&wrapped_key_id,
|
||||
"Department::MKG || Department::FIN"
|
||||
)
|
||||
.await
|
||||
.is_err()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::similar_names)]
|
||||
fn test_cc() -> CliResult<()> {
|
||||
let access_structure = access_structure_from_json_file(&PathBuf::from(
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
))?;
|
||||
|
||||
let cover_crypt = Covercrypt::default();
|
||||
let (mut msk, _mpk) = cover_crypt.setup()?;
|
||||
msk.access_structure = access_structure;
|
||||
let mpk = cover_crypt.update_msk(&mut msk)?;
|
||||
test_serialization(&msk).unwrap();
|
||||
|
||||
let access_policy = "(Department::MKG || Department::FIN) && Security Level::Top Secret";
|
||||
let access_policy = AccessPolicy::parse(access_policy)?;
|
||||
let uk = cover_crypt.generate_user_secret_key(&mut msk, &access_policy)?;
|
||||
let uk_bytes = uk.serialize()?;
|
||||
let uk = UserSecretKey::deserialize(&uk_bytes)?;
|
||||
|
||||
let encryption_policy = "Department::MKG && Security Level::Confidential";
|
||||
let ad = Some("myid".as_ref());
|
||||
let (_secret, encrypted_header) = EncryptedHeader::generate(
|
||||
&cover_crypt,
|
||||
&mpk,
|
||||
&AccessPolicy::parse(encryption_policy)?,
|
||||
None,
|
||||
ad,
|
||||
)?;
|
||||
let encrypted_header = encrypted_header.serialize()?.to_vec();
|
||||
|
||||
let mut de = Deserializer::new(&encrypted_header);
|
||||
let encrypted_header = EncryptedHeader::read(&mut de)?;
|
||||
|
||||
let _plaintext_header = encrypted_header.decrypt(&cover_crypt, &uk, ad)?;
|
||||
|
||||
let ap = AccessPolicy::parse("Department::MKG || Department::FIN")?;
|
||||
let _mpk = cover_crypt.rekey(&mut msk, &ap)?;
|
||||
|
||||
let msk_bytes = msk.serialize()?;
|
||||
let _my_msk = MasterSecretKey::deserialize(&msk_bytes)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_enc_dec_rekey() -> CliResult<()> {
|
||||
let ctx = start_default_test_kms_server().await;
|
||||
// create a temp dir
|
||||
let tmp_dir = TempDir::new()?;
|
||||
let tmp_path = tmp_dir.path();
|
||||
|
||||
let input_file = PathBuf::from("../../test_data/plain.txt");
|
||||
let output_file_before = tmp_path.join("plain.before.enc");
|
||||
let recovered_file = tmp_path.join("plain.txt");
|
||||
|
||||
// generate a new master key pair
|
||||
let (master_secret_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--specification",
|
||||
"../../test_data/access_structure.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
let user_decryption_key_id = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_secret_key_id,
|
||||
"Department::MKG || Department::FIN",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
||||
encrypt(
|
||||
&ctx.owner_client_conf_path,
|
||||
&[input_file.to_str().unwrap()],
|
||||
&master_public_key_id,
|
||||
"Department::MKG",
|
||||
Some(output_file_before.to_str().unwrap()),
|
||||
Some("myid"),
|
||||
)?;
|
||||
|
||||
// the user key should be able to decrypt the file
|
||||
decrypt(
|
||||
&ctx.owner_client_conf_path,
|
||||
&[output_file_before.to_str().unwrap()],
|
||||
&user_decryption_key_id,
|
||||
Some(recovered_file.to_str().unwrap()),
|
||||
Some("myid"),
|
||||
)?;
|
||||
|
||||
// export the user_decryption_key
|
||||
let exported_user_decryption_key_file = tmp_path.join("exported_user_decryption.key");
|
||||
export_key(ExportKeyParams {
|
||||
cli_conf_path: ctx.owner_client_conf_path.clone(),
|
||||
sub_command: SUB_COMMAND.to_owned(),
|
||||
key_id: user_decryption_key_id,
|
||||
key_file: exported_user_decryption_key_file
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
..Default::default()
|
||||
})?;
|
||||
|
||||
// rekey the attributes
|
||||
rekey(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_secret_key_id,
|
||||
"Department::MKG || Department::FIN",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_rekey_prune() -> CliResult<()> {
|
||||
let ctx = start_default_test_kms_server().await;
|
||||
|
@ -173,16 +282,16 @@ async fn test_rekey_prune() -> CliResult<()> {
|
|||
let recovered_file = tmp_path.join("plain.txt");
|
||||
|
||||
// generate a new master key pair
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
let (master_secret_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
let user_decryption_key = create_user_decryption_key(
|
||||
let user_decryption_key_id = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"(Department::MKG || Department::FIN) && Security Level::Top Secret",
|
||||
&[],
|
||||
false,
|
||||
|
@ -201,7 +310,7 @@ async fn test_rekey_prune() -> CliResult<()> {
|
|||
decrypt(
|
||||
&ctx.owner_client_conf_path,
|
||||
&[output_file_before.to_str().unwrap()],
|
||||
&user_decryption_key,
|
||||
&user_decryption_key_id,
|
||||
Some(recovered_file.to_str().unwrap()),
|
||||
Some("myid"),
|
||||
)?;
|
||||
|
@ -211,7 +320,7 @@ async fn test_rekey_prune() -> CliResult<()> {
|
|||
export_key(ExportKeyParams {
|
||||
cli_conf_path: ctx.owner_client_conf_path.clone(),
|
||||
sub_command: SUB_COMMAND.to_owned(),
|
||||
key_id: user_decryption_key.to_string(),
|
||||
key_id: user_decryption_key_id.to_string(),
|
||||
key_file: exported_user_decryption_key_file
|
||||
.to_str()
|
||||
.unwrap()
|
||||
|
@ -222,10 +331,9 @@ async fn test_rekey_prune() -> CliResult<()> {
|
|||
// rekey the attributes
|
||||
rekey(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"Department::MKG || Department::FIN",
|
||||
)
|
||||
.await?;
|
||||
)?;
|
||||
|
||||
// encrypt again after rekeying
|
||||
encrypt(
|
||||
|
@ -241,7 +349,7 @@ async fn test_rekey_prune() -> CliResult<()> {
|
|||
decrypt(
|
||||
&ctx.owner_client_conf_path,
|
||||
&[output_file_after.to_str().unwrap()],
|
||||
&user_decryption_key,
|
||||
&user_decryption_key_id,
|
||||
Some(recovered_file.to_str().unwrap()),
|
||||
Some("myid"),
|
||||
)?;
|
||||
|
@ -249,13 +357,13 @@ async fn test_rekey_prune() -> CliResult<()> {
|
|||
decrypt(
|
||||
&ctx.owner_client_conf_path,
|
||||
&[output_file_before.to_str().unwrap()],
|
||||
&user_decryption_key,
|
||||
&user_decryption_key_id,
|
||||
Some(recovered_file.to_str().unwrap()),
|
||||
Some("myid"),
|
||||
)?;
|
||||
|
||||
// import the non rotated user_decryption_key
|
||||
let old_user_decryption_key = import_key(ImportKeyParams {
|
||||
let old_user_decryption_key_id = import_key(ImportKeyParams {
|
||||
cli_conf_path: ctx.owner_client_conf_path.clone(),
|
||||
sub_command: SUB_COMMAND.to_owned(),
|
||||
key_file: exported_user_decryption_key_file
|
||||
|
@ -270,7 +378,7 @@ async fn test_rekey_prune() -> CliResult<()> {
|
|||
decrypt(
|
||||
&ctx.owner_client_conf_path,
|
||||
&[output_file_after.to_str().unwrap()],
|
||||
&old_user_decryption_key,
|
||||
&old_user_decryption_key_id,
|
||||
Some(recovered_file.to_str().unwrap()),
|
||||
Some("myid"),
|
||||
)
|
||||
|
@ -280,7 +388,7 @@ async fn test_rekey_prune() -> CliResult<()> {
|
|||
decrypt(
|
||||
&ctx.owner_client_conf_path,
|
||||
&[output_file_before.to_str().unwrap()],
|
||||
&old_user_decryption_key,
|
||||
&old_user_decryption_key_id,
|
||||
Some(recovered_file.to_str().unwrap()),
|
||||
Some("myid"),
|
||||
)?;
|
||||
|
@ -288,16 +396,15 @@ async fn test_rekey_prune() -> CliResult<()> {
|
|||
// prune the attributes
|
||||
prune(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"Department::MKG || Department::FIN",
|
||||
)
|
||||
.await?;
|
||||
)?;
|
||||
|
||||
// the user key should be able to decrypt the new file
|
||||
decrypt(
|
||||
&ctx.owner_client_conf_path,
|
||||
&[output_file_after.to_str().unwrap()],
|
||||
&user_decryption_key,
|
||||
&user_decryption_key_id,
|
||||
Some(recovered_file.to_str().unwrap()),
|
||||
Some("myid"),
|
||||
)?;
|
||||
|
@ -307,7 +414,7 @@ async fn test_rekey_prune() -> CliResult<()> {
|
|||
decrypt(
|
||||
&ctx.owner_client_conf_path,
|
||||
&[output_file_before.to_str().unwrap()],
|
||||
&user_decryption_key,
|
||||
&user_decryption_key_id,
|
||||
Some(recovered_file.to_str().unwrap()),
|
||||
Some("myid"),
|
||||
)
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
|
||||
pub(crate) fn create_user_decryption_key(
|
||||
cli_conf_path: &str,
|
||||
master_private_key_id: &str,
|
||||
master_secret_key_id: &str,
|
||||
access_policy: &str,
|
||||
tags: &[&str],
|
||||
sensitive: bool,
|
||||
|
@ -27,7 +27,7 @@ pub(crate) fn create_user_decryption_key(
|
|||
let mut args = vec![
|
||||
"keys",
|
||||
"create-user-key",
|
||||
master_private_key_id,
|
||||
master_secret_key_id,
|
||||
access_policy,
|
||||
];
|
||||
// add tags
|
||||
|
@ -58,10 +58,10 @@ pub(crate) async fn test_user_decryption_key() -> CliResult<()> {
|
|||
let ctx = start_default_test_kms_server().await;
|
||||
|
||||
// generate a new master key pair
|
||||
let (master_private_key_id, _) = create_cc_master_key_pair(
|
||||
let (master_secret_key_id, _) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
@ -69,7 +69,7 @@ pub(crate) async fn test_user_decryption_key() -> CliResult<()> {
|
|||
// and a user key
|
||||
let user_key_id = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"(Department::MKG || Department::FIN) && Security Level::Top Secret",
|
||||
&[],
|
||||
false,
|
||||
|
@ -84,10 +84,10 @@ pub(crate) async fn test_user_decryption_key_error() -> CliResult<()> {
|
|||
let ctx = start_default_test_kms_server().await;
|
||||
|
||||
// generate a new master key pair
|
||||
let (master_private_key_id, _) = create_cc_master_key_pair(
|
||||
let (master_secret_key_id, _) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
@ -95,7 +95,7 @@ pub(crate) async fn test_user_decryption_key_error() -> CliResult<()> {
|
|||
// bad attributes
|
||||
let err = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
&master_private_key_id,
|
||||
&master_secret_key_id,
|
||||
"(Department::MKG || Department::FIN) && Security Level::Top SecretZZZZZZ",
|
||||
&[],
|
||||
false,
|
||||
|
@ -104,10 +104,10 @@ pub(crate) async fn test_user_decryption_key_error() -> CliResult<()> {
|
|||
.unwrap();
|
||||
assert!(
|
||||
err.to_string()
|
||||
.contains("attribute not found: Security Level::Top SecretZZZZZZ")
|
||||
.contains("attribute not found: Top SecretZZZZZZ")
|
||||
);
|
||||
|
||||
// bad master private key
|
||||
// bad master secret key
|
||||
let err = create_user_decryption_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
"BAD_KEY",
|
||||
|
@ -119,7 +119,7 @@ pub(crate) async fn test_user_decryption_key_error() -> CliResult<()> {
|
|||
.unwrap();
|
||||
assert!(
|
||||
err.to_string()
|
||||
.contains("no Covercrypt master private key found for: BAD_KEY")
|
||||
.contains("no Covercrypt master secret key found for: BAD_KEY")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -354,7 +354,7 @@ async fn test_rsa_encrypt_decrypt_using_tags() -> CliResult<()> {
|
|||
fs::remove_file(&output_file).ok();
|
||||
assert!(!output_file.exists());
|
||||
|
||||
let (_private_key_id, _public_key_id) = create_rsa_key_pair(
|
||||
let (private_key_id, public_key_id) = create_rsa_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
&RsaKeyPairOptions {
|
||||
tags: HashSet::from(["tag_rsa".to_string()]),
|
||||
|
@ -365,7 +365,7 @@ async fn test_rsa_encrypt_decrypt_using_tags() -> CliResult<()> {
|
|||
encrypt(
|
||||
&ctx.owner_client_conf_path,
|
||||
&[input_file.to_str().unwrap()],
|
||||
"[\"tag_rsa\"]",
|
||||
&public_key_id,
|
||||
RsaEncryptionAlgorithm::CkmRsaPkcsOaep,
|
||||
Some(HashFn::Sha256),
|
||||
Some(output_file.to_str().unwrap()),
|
||||
|
@ -376,7 +376,7 @@ async fn test_rsa_encrypt_decrypt_using_tags() -> CliResult<()> {
|
|||
decrypt(
|
||||
&ctx.owner_client_conf_path,
|
||||
output_file.to_str().unwrap(),
|
||||
"[\"tag_rsa\"]",
|
||||
&private_key_id,
|
||||
RsaEncryptionAlgorithm::CkmRsaPkcsOaep,
|
||||
Some(HashFn::Sha256),
|
||||
Some(recovered_file.to_str().unwrap()),
|
||||
|
|
|
@ -274,8 +274,8 @@ async fn test_destroy_cover_crypt() -> CliResult<()> {
|
|||
// generate a new master key pair
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
@ -332,8 +332,8 @@ async fn test_destroy_cover_crypt() -> CliResult<()> {
|
|||
// generate a new master key pair
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
@ -390,8 +390,8 @@ async fn test_destroy_cover_crypt() -> CliResult<()> {
|
|||
// generate a new master key pair
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
|
|
@ -380,8 +380,8 @@ pub(crate) async fn test_export_covercrypt() -> CliResult<()> {
|
|||
// generate a new master key pair
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
@ -439,8 +439,8 @@ pub(crate) async fn test_export_error_cover_crypt() -> CliResult<()> {
|
|||
// generate a new master key pair
|
||||
let (master_private_key_id, _master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
@ -716,13 +716,13 @@ pub(crate) async fn test_sensitive_covercrypt_key() -> CliResult<()> {
|
|||
// generate a new master key pair
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
true,
|
||||
)?;
|
||||
|
||||
// Master private key should not be exportable
|
||||
// master secret key should not be exportable
|
||||
assert!(
|
||||
export_key(ExportKeyParams {
|
||||
cli_conf_path: ctx.owner_client_conf_path.clone(),
|
||||
|
@ -738,7 +738,7 @@ pub(crate) async fn test_sensitive_covercrypt_key() -> CliResult<()> {
|
|||
.is_err()
|
||||
);
|
||||
|
||||
// Master public key should not be exportable
|
||||
// Master public key should be exportable
|
||||
assert!(
|
||||
export_key(ExportKeyParams {
|
||||
cli_conf_path: ctx.owner_client_conf_path.clone(),
|
||||
|
|
|
@ -153,8 +153,8 @@ pub(crate) async fn test_generate_export_import() -> CliResult<()> {
|
|||
// Covercrypt import/export test
|
||||
let (private_key_id, _public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use cloudproof::reexport::crypto_core::{
|
||||
use cosmian_crypto_core::{
|
||||
reexport::rand_core::{RngCore, SeedableRng},
|
||||
CsRng,
|
||||
};
|
||||
|
@ -60,8 +60,8 @@ pub(crate) async fn test_import_export_wrap_rfc_5649() -> CliResult<()> {
|
|||
// test CC
|
||||
let (private_key_id, _public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
@ -147,8 +147,8 @@ pub(crate) async fn test_import_export_wrap_ecies() -> CliResult<()> {
|
|||
// test CC
|
||||
let (private_key_id, _public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
|
|
@ -75,8 +75,8 @@ pub(crate) async fn test_locate_cover_crypt() -> CliResult<()> {
|
|||
// generate a new master key pair
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&["test_cc"],
|
||||
false,
|
||||
)?;
|
||||
|
@ -388,8 +388,8 @@ pub(crate) async fn test_locate_grant() -> CliResult<()> {
|
|||
// generate a new master key pair
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&["test_grant"],
|
||||
false,
|
||||
)?;
|
||||
|
|
|
@ -156,8 +156,8 @@ async fn test_revoke_cover_crypt() -> CliResult<()> {
|
|||
// generate a new master key pair
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
@ -196,8 +196,8 @@ async fn test_revoke_cover_crypt() -> CliResult<()> {
|
|||
// generate a new master key pair
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
@ -236,8 +236,8 @@ async fn test_revoke_cover_crypt() -> CliResult<()> {
|
|||
// generate a new master key pair
|
||||
let (master_private_key_id, master_public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
|
||||
use assert_cmd::prelude::CommandCargoExt;
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use cloudproof::reexport::crypto_core::{
|
||||
use cosmian_crypto_core::{
|
||||
reexport::rand_core::{RngCore, SeedableRng},
|
||||
CsRng,
|
||||
};
|
||||
|
@ -138,8 +138,8 @@ pub(crate) async fn test_password_wrap_import() -> CliResult<()> {
|
|||
// CC
|
||||
let (private_key_id, _public_key_id) = create_cc_master_key_pair(
|
||||
&ctx.owner_client_conf_path,
|
||||
"--policy-specifications",
|
||||
"../../test_data/policy_specifications.json",
|
||||
"--specification",
|
||||
"../../test_data/access_structure_specifications.json",
|
||||
&[],
|
||||
false,
|
||||
)?;
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::process::Command;
|
|||
|
||||
use assert_cmd::prelude::*;
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use cloudproof::reexport::crypto_core::{
|
||||
use cosmian_crypto_core::{
|
||||
reexport::rand_core::{RngCore, SeedableRng},
|
||||
CsRng,
|
||||
};
|
||||
|
|
|
@ -262,7 +262,7 @@ async fn test_encrypt_decrypt_with_tags() -> CliResult<()> {
|
|||
let tmp_path = tmp_dir.path();
|
||||
|
||||
let ctx = start_default_test_kms_server().await;
|
||||
let _key_id = create_symmetric_key(
|
||||
let key_id = create_symmetric_key(
|
||||
&ctx.owner_client_conf_path,
|
||||
CreateKeyAction {
|
||||
tags: vec!["tag_sym".to_owned()],
|
||||
|
@ -285,7 +285,7 @@ async fn test_encrypt_decrypt_with_tags() -> CliResult<()> {
|
|||
encrypt(
|
||||
&ctx.owner_client_conf_path,
|
||||
input_file.to_str().unwrap(),
|
||||
"[\"tag_sym\"]",
|
||||
&key_id,
|
||||
DataEncryptionAlgorithm::Chacha20Poly1305,
|
||||
None,
|
||||
Some(output_file.to_str().unwrap()),
|
||||
|
@ -296,7 +296,7 @@ async fn test_encrypt_decrypt_with_tags() -> CliResult<()> {
|
|||
decrypt(
|
||||
&ctx.owner_client_conf_path,
|
||||
output_file.to_str().unwrap(),
|
||||
"[\"tag_sym\"]",
|
||||
&key_id,
|
||||
DataEncryptionAlgorithm::Chacha20Poly1305,
|
||||
None,
|
||||
Some(recovered_file.to_str().unwrap()),
|
||||
|
|
|
@ -16,8 +16,8 @@ doctest = false
|
|||
fips = ["cosmian_kmip/fips"]
|
||||
|
||||
[dependencies]
|
||||
cloudproof = { workspace = true }
|
||||
cosmian_config_utils = { workspace = true }
|
||||
cosmian_crypto_core = { workspace = true }
|
||||
cosmian_http_client = { workspace = true }
|
||||
cosmian_kmip = { path = "../kmip" }
|
||||
cosmian_kms_access = { path = "../access" }
|
||||
|
|
|
@ -118,8 +118,8 @@ impl From<KmipError> for KmsClientError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<cloudproof::reexport::crypto_core::CryptoCoreError> for KmsClientError {
|
||||
fn from(e: cloudproof::reexport::crypto_core::CryptoCoreError) -> Self {
|
||||
impl From<cosmian_crypto_core::CryptoCoreError> for KmsClientError {
|
||||
fn from(e: cosmian_crypto_core::CryptoCoreError) -> Self {
|
||||
Self::UnexpectedError(e.to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use cloudproof::reexport::crypto_core::bytes_ser_de::{Deserializer, Serializer};
|
||||
use cosmian_crypto_core::bytes_ser_de::{Deserializer, Serializer};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
use crate::{
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::{
|
|||
///
|
||||
/// If the `import_attributes` are not specified,
|
||||
/// the attributes of the object are used, if any.
|
||||
pub async fn import_object<'a, T: IntoIterator<Item = impl AsRef<str>>>(
|
||||
pub async fn import_object<T: IntoIterator<Item = impl AsRef<str>>>(
|
||||
kms_rest_client: &KmsClient,
|
||||
object_id: Option<String>,
|
||||
object: Object,
|
||||
|
|
|
@ -13,7 +13,6 @@ rust-version.workspace = true
|
|||
doctest = false
|
||||
|
||||
[features]
|
||||
pyo3 = ["dep:pyo3"]
|
||||
# Enable FIPS module feature build. KMS builds in FIPS mode when this is enabled.
|
||||
fips = ["cosmian_kmip/fips"]
|
||||
|
||||
|
@ -21,7 +20,8 @@ fips = ["cosmian_kmip/fips"]
|
|||
aes-gcm-siv = "0.11.1"
|
||||
argon2 = "0.5"
|
||||
base64 = { workspace = true }
|
||||
cloudproof = { workspace = true }
|
||||
cosmian_cover_crypt = { workspace = true }
|
||||
cosmian_crypto_core = { workspace = true, features = ["ecies", "blake", "chacha"] }
|
||||
cosmian_kmip = { path = "../kmip" }
|
||||
cosmian_logger = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
|
@ -32,7 +32,6 @@ num-bigint-dig = { workspace = true, features = [
|
|||
"zeroize",
|
||||
] }
|
||||
openssl = { workspace = true }
|
||||
pyo3 = { workspace = true, optional = true }
|
||||
rust-ini = "0.21"
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
@ -42,4 +41,4 @@ x509-parser = { workspace = true }
|
|||
zeroize = { workspace = true, features = ["zeroize_derive", "serde"] }
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["rust-ini", "sha3"]
|
||||
ignored = ["rust-ini"]
|
||||
|
|
51
crate/crypto/src/crypto/cover_crypt/access_structure.rs
Normal file
51
crate/crypto/src/crypto/cover_crypt/access_structure.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
use std::{collections::HashMap, path::Path};
|
||||
|
||||
use cosmian_cover_crypt::{AccessStructure, EncryptionHint, QualifiedAttribute};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{error::result::CryptoResult, CryptoError};
|
||||
|
||||
pub fn access_structure_from_json_file(
|
||||
specs_filename: &impl AsRef<Path>,
|
||||
) -> CryptoResult<AccessStructure> {
|
||||
let access_structure = std::fs::read_to_string(specs_filename.as_ref())?;
|
||||
access_structure_from_str(&access_structure)
|
||||
}
|
||||
|
||||
pub fn access_structure_from_str(access_structure_str: &str) -> CryptoResult<AccessStructure> {
|
||||
let access_structure_json: HashMap<String, Vec<String>> =
|
||||
serde_json::from_str(access_structure_str).map_err(|e| {
|
||||
CryptoError::Default(format!(
|
||||
"failed parsing the access structure from the string: {e}"
|
||||
))
|
||||
})?;
|
||||
|
||||
let mut access_structure = AccessStructure::new();
|
||||
for (dimension, attributes) in &access_structure_json {
|
||||
if dimension.contains("::<") {
|
||||
let trim_key_name = dimension.trim_end_matches("::<");
|
||||
access_structure.add_hierarchy(trim_key_name.to_owned())?;
|
||||
} else {
|
||||
access_structure.add_anarchy(dimension.clone())?;
|
||||
}
|
||||
|
||||
// Reversing the iterator is necessary because hierarchical attributes
|
||||
// are declared in increasing order but inserted in decreasing order
|
||||
// when `None` is passed as `after`.
|
||||
for name in attributes.iter().rev() {
|
||||
let attribute = QualifiedAttribute {
|
||||
dimension: dimension.trim_end_matches("::<").to_owned(),
|
||||
name: name.trim_end_matches("::+").to_owned(),
|
||||
};
|
||||
let encryption_hint = if name.contains("::+") {
|
||||
EncryptionHint::Hybridized
|
||||
} else {
|
||||
EncryptionHint::Classic
|
||||
};
|
||||
debug!("attribute: {attribute:?}, encryption_hint: {encryption_hint:?}");
|
||||
access_structure.add_attribute(attribute, encryption_hint, None)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(access_structure)
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use cloudproof::reexport::cover_crypt::abe_policy::{self, AccessPolicy, EncryptionHint, Policy};
|
||||
use cosmian_cover_crypt::{AccessPolicy, AccessStructure, EncryptionHint, QualifiedAttribute};
|
||||
use cosmian_crypto_core::bytes_ser_de::Serializable;
|
||||
use cosmian_kmip::kmip_2_1::{
|
||||
extra::VENDOR_ID_COSMIAN,
|
||||
kmip_types::{Attributes, VendorAttribute},
|
||||
|
@ -8,54 +9,103 @@ use serde::{Deserialize, Serialize};
|
|||
use crate::error::CryptoError;
|
||||
|
||||
pub const VENDOR_ATTR_COVER_CRYPT_ATTR: &str = "cover_crypt_attributes";
|
||||
pub const VENDOR_ATTR_COVER_CRYPT_POLICY: &str = "cover_crypt_policy";
|
||||
pub const VENDOR_ATTR_COVER_CRYPT_ACCESS_STRUCTURE: &str = "cover_crypt_access_structure";
|
||||
pub const VENDOR_ATTR_COVER_CRYPT_ACCESS_POLICY: &str = "cover_crypt_access_policy";
|
||||
pub const VENDOR_ATTR_COVER_CRYPT_REKEY_ACTION: &str = "cover_crypt_rekey_action";
|
||||
|
||||
/// Convert an policy to a vendor attribute
|
||||
pub fn policy_as_vendor_attribute(policy: &Policy) -> Result<VendorAttribute, CryptoError> {
|
||||
/// Convert an access structure to a vendor attribute
|
||||
pub fn access_structure_as_vendor_attribute(
|
||||
access_structure: &AccessStructure,
|
||||
) -> Result<VendorAttribute, CryptoError> {
|
||||
Ok(VendorAttribute {
|
||||
vendor_identification: VENDOR_ID_COSMIAN.to_owned(),
|
||||
attribute_name: VENDOR_ATTR_COVER_CRYPT_POLICY.to_owned(),
|
||||
attribute_value: Vec::<u8>::try_from(policy).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"failed convert the CoverCrypt policy to bytes: {e}"
|
||||
))
|
||||
})?,
|
||||
attribute_name: VENDOR_ATTR_COVER_CRYPT_ACCESS_STRUCTURE.to_owned(),
|
||||
attribute_value: access_structure
|
||||
.serialize()
|
||||
.map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"failed convert the Covercrypt access structure to bytes: {e}"
|
||||
))
|
||||
})?
|
||||
.to_vec(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract an `CoverCrypt` policy from attributes
|
||||
pub fn policy_from_attributes(attributes: &Attributes) -> Result<Policy, CryptoError> {
|
||||
/// Extract an `Covercrypt` access structure from attributes
|
||||
pub fn access_structure_from_attributes(
|
||||
attributes: &Attributes,
|
||||
) -> Result<AccessStructure, CryptoError> {
|
||||
attributes
|
||||
.get_vendor_attribute_value(VENDOR_ID_COSMIAN, VENDOR_ATTR_COVER_CRYPT_POLICY)
|
||||
.get_vendor_attribute_value(VENDOR_ID_COSMIAN, VENDOR_ATTR_COVER_CRYPT_ACCESS_STRUCTURE)
|
||||
.map_or_else(
|
||||
|| {
|
||||
Err(CryptoError::Kmip(
|
||||
"the attributes do not contain a CoverCrypt Policy".to_owned(),
|
||||
"the attributes do not contain a Covercrypt access structure".to_owned(),
|
||||
))
|
||||
},
|
||||
|bytes| {
|
||||
Policy::parse_and_convert(bytes).map_err(|e| {
|
||||
AccessStructure::deserialize(bytes).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"failed deserializing the CoverCrypt Policy from the attributes: {e}"
|
||||
"failed deserializing the Covercrypt access structure from the \
|
||||
attributes: {e}"
|
||||
))
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Add or replace an `CoverCrypt` policy in attributes in place
|
||||
pub fn upsert_policy_in_attributes(
|
||||
/// Add or replace an access policy in attributes in place
|
||||
pub fn upsert_access_structure_in_attributes(
|
||||
attributes: &mut Attributes,
|
||||
policy: &Policy,
|
||||
access_structure: &AccessStructure,
|
||||
) -> Result<(), CryptoError> {
|
||||
let va = policy_as_vendor_attribute(policy)?;
|
||||
attributes.remove_vendor_attribute(VENDOR_ID_COSMIAN, VENDOR_ATTR_COVER_CRYPT_POLICY);
|
||||
let va = access_structure_as_vendor_attribute(access_structure)?;
|
||||
attributes.remove_vendor_attribute(VENDOR_ID_COSMIAN, VENDOR_ATTR_COVER_CRYPT_ACCESS_STRUCTURE);
|
||||
attributes.add_vendor_attribute(va);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Convert a list of `Covercrypt` qualified attributes to a vendor attribute.
|
||||
pub fn qualified_attributes_as_vendor_attributes(
|
||||
attributes: &[QualifiedAttribute],
|
||||
) -> Result<VendorAttribute, CryptoError> {
|
||||
Ok(VendorAttribute {
|
||||
vendor_identification: VENDOR_ID_COSMIAN.to_owned(),
|
||||
attribute_name: VENDOR_ATTR_COVER_CRYPT_ATTR.to_owned(),
|
||||
attribute_value: serde_json::to_vec(&attributes).map_err(|e| {
|
||||
CryptoError::Kmip(format!("failed serializing the Covercrypt attributes: {e}"))
|
||||
})?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract qualified attributes from the given KMIP attributes.
|
||||
pub fn qualified_attributes_from_attributes(
|
||||
attributes: &Attributes,
|
||||
) -> Result<Vec<QualifiedAttribute>, CryptoError> {
|
||||
let bytes = attributes
|
||||
.get_vendor_attribute_value(VENDOR_ID_COSMIAN, VENDOR_ATTR_COVER_CRYPT_ATTR)
|
||||
.ok_or_else(|| {
|
||||
CryptoError::Kmip(
|
||||
"the attributes do not contain Covercrypt (vendor) Attributes".to_owned(),
|
||||
)
|
||||
})?;
|
||||
let attribute_strings = serde_json::from_slice::<Vec<String>>(bytes).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"failed reading the Covercrypt attribute strings from the attributes bytes: {e}"
|
||||
))
|
||||
})?;
|
||||
attribute_strings
|
||||
.iter()
|
||||
.map(|attr| {
|
||||
QualifiedAttribute::try_from(attr.as_str()).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"failed deserializing the Covercrypt attribute: {e}"
|
||||
))
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Convert an access policy to a vendor attribute
|
||||
pub fn access_policy_as_vendor_attribute(
|
||||
access_policy: &str,
|
||||
|
@ -67,49 +117,7 @@ pub fn access_policy_as_vendor_attribute(
|
|||
})
|
||||
}
|
||||
|
||||
/// Convert from `CoverCrypt` policy attributes to vendor attributes
|
||||
pub fn attributes_as_vendor_attribute(
|
||||
attributes: &[abe_policy::Attribute],
|
||||
) -> Result<VendorAttribute, CryptoError> {
|
||||
Ok(VendorAttribute {
|
||||
vendor_identification: VENDOR_ID_COSMIAN.to_owned(),
|
||||
attribute_name: VENDOR_ATTR_COVER_CRYPT_ATTR.to_owned(),
|
||||
attribute_value: serde_json::to_vec(&attributes).map_err(|e| {
|
||||
CryptoError::Kmip(format!("failed serializing the CoverCrypt attributes: {e}"))
|
||||
})?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert from vendor attributes to `CoverCrypt` policy attributes
|
||||
pub fn attributes_from_attributes(
|
||||
attributes: &Attributes,
|
||||
) -> Result<Vec<abe_policy::Attribute>, CryptoError> {
|
||||
if let Some(bytes) =
|
||||
attributes.get_vendor_attribute_value(VENDOR_ID_COSMIAN, VENDOR_ATTR_COVER_CRYPT_ATTR)
|
||||
{
|
||||
let attribute_strings = serde_json::from_slice::<Vec<String>>(bytes).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"failed reading the CoverCrypt attribute strings from the attributes bytes: {e}"
|
||||
))
|
||||
})?;
|
||||
let mut policy_attributes = Vec::with_capacity(attribute_strings.len());
|
||||
for attr in attribute_strings {
|
||||
let attr = abe_policy::Attribute::try_from(attr.as_str()).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"failed deserializing the CoverCrypt attribute: {e}"
|
||||
))
|
||||
})?;
|
||||
policy_attributes.push(attr);
|
||||
}
|
||||
Ok(policy_attributes)
|
||||
} else {
|
||||
Err(CryptoError::Kmip(
|
||||
"the attributes do not contain CoverCrypt (vendor) Attributes".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract an `CoverCrypt` Access policy from attributes
|
||||
/// Extract an `Covercrypt` Access policy from attributes
|
||||
pub fn access_policy_from_attributes(attributes: &Attributes) -> Result<String, CryptoError> {
|
||||
attributes
|
||||
.get_vendor_attribute_value(VENDOR_ID_COSMIAN, VENDOR_ATTR_COVER_CRYPT_ACCESS_POLICY)
|
||||
|
@ -142,7 +150,7 @@ pub fn upsert_access_policy_in_attributes(
|
|||
}
|
||||
|
||||
pub fn deserialize_access_policy(ap: &str) -> Result<AccessPolicy, CryptoError> {
|
||||
AccessPolicy::from_boolean_expression(ap).map_err(|e| {
|
||||
AccessPolicy::parse(ap).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"failed to deserialize the given Access Policy string: {e}"
|
||||
))
|
||||
|
@ -153,10 +161,10 @@ pub fn deserialize_access_policy(ap: &str) -> Result<AccessPolicy, CryptoError>
|
|||
pub enum RekeyEditAction {
|
||||
RekeyAccessPolicy(String),
|
||||
PruneAccessPolicy(String),
|
||||
RemoveAttribute(Vec<abe_policy::Attribute>),
|
||||
DisableAttribute(Vec<abe_policy::Attribute>),
|
||||
AddAttribute(Vec<(abe_policy::Attribute, EncryptionHint)>),
|
||||
RenameAttribute(Vec<(abe_policy::Attribute, String)>),
|
||||
DeleteAttribute(Vec<QualifiedAttribute>),
|
||||
DisableAttribute(Vec<QualifiedAttribute>),
|
||||
AddAttribute(Vec<(QualifiedAttribute, EncryptionHint, Option<String>)>),
|
||||
RenameAttribute(Vec<(QualifiedAttribute, String)>),
|
||||
}
|
||||
|
||||
/// Convert an edit action to a vendor attribute
|
||||
|
@ -167,12 +175,12 @@ pub fn rekey_edit_action_as_vendor_attribute(
|
|||
vendor_identification: VENDOR_ID_COSMIAN.to_owned(),
|
||||
attribute_name: VENDOR_ATTR_COVER_CRYPT_REKEY_ACTION.to_owned(),
|
||||
attribute_value: serde_json::to_vec(action).map_err(|e| {
|
||||
CryptoError::Kmip(format!("failed serializing the CoverCrypt action: {e}"))
|
||||
CryptoError::Kmip(format!("failed serializing the Covercrypt action: {e}"))
|
||||
})?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract an edit `CoverCrypt` policy action from attributes.
|
||||
/// Extract an edit `Covercrypt` re-key action from attributes.
|
||||
///
|
||||
/// If Covercrypt attributes are specified without an `EditPolicyAction`,
|
||||
/// a `RotateAttributes` action is returned by default to keep backward compatibility.
|
||||
|
@ -190,7 +198,7 @@ pub fn rekey_edit_action_from_attributes(
|
|||
|bytes| {
|
||||
serde_json::from_slice::<RekeyEditAction>(bytes).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"failed reading the CoverCrypt action from the attribute bytes: {e}"
|
||||
"failed reading the Covercrypt action from the attribute bytes: {e}"
|
||||
))
|
||||
})
|
||||
},
|
||||
|
|
|
@ -1,24 +1,28 @@
|
|||
use cloudproof::reexport::{
|
||||
cover_crypt::{CleartextHeader, Covercrypt, EncryptedHeader, UserSecretKey},
|
||||
crypto_core::bytes_ser_de::{Deserializer, Serializable, Serializer},
|
||||
use cosmian_cover_crypt::{api::Covercrypt, traits::KemAc, Error, UserSecretKey, XEnc};
|
||||
use cosmian_crypto_core::{
|
||||
bytes_ser_de::{Deserializer, Serializable, Serializer},
|
||||
Aes256Gcm, Dem, FixedSizeCBytes, Instantiable, Nonce, SymmetricKey,
|
||||
};
|
||||
use cosmian_kmip::kmip_2_1::{
|
||||
kmip_objects::Object,
|
||||
kmip_operations::{Decrypt, DecryptResponse, DecryptedData},
|
||||
kmip_operations::{Decrypt, DecryptResponse},
|
||||
kmip_types::{CryptographicAlgorithm, CryptographicParameters, UniqueIdentifier},
|
||||
};
|
||||
use tracing::{debug, trace};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use super::user_key::unwrap_user_decryption_key_object;
|
||||
use crate::{crypto::DecryptionSystem, error::CryptoError};
|
||||
use crate::{
|
||||
crypto::DecryptionSystem,
|
||||
error::{result::CryptoResult, CryptoError},
|
||||
};
|
||||
|
||||
/// Decrypt a single block of data encrypted using an hybrid encryption mode
|
||||
/// Cannot be used as a stream decipher
|
||||
pub struct CovercryptDecryption {
|
||||
cover_crypt: Covercrypt,
|
||||
user_decryption_key_uid: String,
|
||||
user_decryption_key_bytes: Zeroizing<Vec<u8>>,
|
||||
usk_uid: String,
|
||||
usk_bytes: Zeroizing<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl CovercryptDecryption {
|
||||
|
@ -28,6 +32,7 @@ impl CovercryptDecryption {
|
|||
user_decryption_key: &Object,
|
||||
) -> Result<Self, CryptoError> {
|
||||
trace!("CovercryptDecryption::instantiate entering");
|
||||
|
||||
let (user_decryption_key_bytes, _attributes) =
|
||||
unwrap_user_decryption_key_object(user_decryption_key)?;
|
||||
|
||||
|
@ -38,41 +43,47 @@ impl CovercryptDecryption {
|
|||
|
||||
Ok(Self {
|
||||
cover_crypt,
|
||||
user_decryption_key_uid: user_decryption_key_uid.into(),
|
||||
user_decryption_key_bytes,
|
||||
usk_uid: user_decryption_key_uid.into(),
|
||||
usk_bytes: user_decryption_key_bytes,
|
||||
})
|
||||
}
|
||||
|
||||
/// Decrypt a single payload
|
||||
fn decrypt(
|
||||
fn single_decrypt(
|
||||
&self,
|
||||
encrypted_bytes: &[u8],
|
||||
aead: Option<&[u8]>,
|
||||
user_decryption_key: &UserSecretKey,
|
||||
) -> Result<(CleartextHeader, Zeroizing<Vec<u8>>), CryptoError> {
|
||||
ad: Option<&[u8]>,
|
||||
usk: &UserSecretKey,
|
||||
) -> CryptoResult<Zeroizing<Vec<u8>>> {
|
||||
trace!("CovercryptDecryption: decrypt: ad: {ad:?}");
|
||||
|
||||
let mut de = Deserializer::new(encrypted_bytes);
|
||||
let encrypted_header = EncryptedHeader::read(&mut de)
|
||||
.map_err(|e| CryptoError::Kmip(format!("Bad or corrupted encrypted data: {e}")))?;
|
||||
let encrypted_block = de.finalize();
|
||||
|
||||
let header = encrypted_header
|
||||
.decrypt(&self.cover_crypt, user_decryption_key, aead)
|
||||
.map_err(|e| CryptoError::Kmip(e.to_string()))?;
|
||||
|
||||
let cleartext = Zeroizing::from(
|
||||
self.cover_crypt
|
||||
.decrypt(&header.symmetric_key, &encrypted_block, aead)
|
||||
.map_err(|e| CryptoError::Kmip(e.to_string()))?,
|
||||
trace!(
|
||||
"CovercryptDecryption: encrypted_bytes len: {}",
|
||||
encrypted_bytes.len()
|
||||
);
|
||||
|
||||
let enc = XEnc::read(&mut de)?;
|
||||
|
||||
trace!("encrypted_header parsed");
|
||||
|
||||
let seed = self.cover_crypt.decaps(usk, &enc)?.ok_or_else(|| {
|
||||
Error::OperationNotPermitted("insufficient rights to open encapsulation".to_owned())
|
||||
})?;
|
||||
|
||||
let key = SymmetricKey::derive(&seed, b"Covercrypt AEAD key")?;
|
||||
// The rest of the bytes is the encrypted payload.
|
||||
let ctx = de.finalize();
|
||||
let ptx = aead_decrypt(&key, &ctx, ad)?;
|
||||
|
||||
debug!(
|
||||
"Decrypted data with user key {} of len (CT/Enc): {}/{}",
|
||||
&self.user_decryption_key_uid,
|
||||
cleartext.len(),
|
||||
encrypted_bytes.len(),
|
||||
"Decrypted data with user key {} of len (Plain/Enc): {}/{}",
|
||||
&self.usk_uid,
|
||||
ptx.len(),
|
||||
enc.length(),
|
||||
);
|
||||
|
||||
Ok((header, cleartext))
|
||||
Ok(ptx)
|
||||
}
|
||||
|
||||
/// Decrypt multiple LEB128-serialized payloads
|
||||
|
@ -102,13 +113,12 @@ impl CovercryptDecryption {
|
|||
fn bulk_decrypt(
|
||||
&self,
|
||||
encrypted_bytes: &[u8],
|
||||
aead: Option<&[u8]>,
|
||||
user_decryption_key: &UserSecretKey,
|
||||
) -> Result<(CleartextHeader, Zeroizing<Vec<u8>>), CryptoError> {
|
||||
ad: Option<&[u8]>,
|
||||
usk: &UserSecretKey,
|
||||
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
|
||||
let mut de = Deserializer::new(encrypted_bytes);
|
||||
let mut ser = Serializer::new();
|
||||
|
||||
// number of chunks of encrypted data to decrypt
|
||||
let nb_chunks = {
|
||||
let len = de.read_leb128_u64()?;
|
||||
ser.write_leb128_u64(len)?;
|
||||
|
@ -119,92 +129,72 @@ impl CovercryptDecryption {
|
|||
})?
|
||||
};
|
||||
|
||||
let mut cleartext_header = None;
|
||||
|
||||
for _ in 0..nb_chunks {
|
||||
let chunk_data = de.read_vec_as_ref()?;
|
||||
|
||||
let (encrypted_header, encrypted_block) = {
|
||||
let mut de_chunk = Deserializer::new(chunk_data);
|
||||
let encrypted_header = EncryptedHeader::read(&mut de_chunk).map_err(|e| {
|
||||
CryptoError::Kmip(format!("Bad or corrupted bulk encrypted data: {e}"))
|
||||
})?;
|
||||
let encrypted_block = de_chunk.finalize();
|
||||
(encrypted_header, encrypted_block)
|
||||
};
|
||||
|
||||
let header = encrypted_header
|
||||
.decrypt(&self.cover_crypt, user_decryption_key, aead)
|
||||
.map_err(|e| CryptoError::Kmip(e.to_string()))?;
|
||||
|
||||
let cleartext = self
|
||||
.cover_crypt
|
||||
.decrypt(&header.symmetric_key, &encrypted_block, aead)
|
||||
.map_err(|e| CryptoError::Kmip(e.to_string()))?;
|
||||
|
||||
// All the headers are the same
|
||||
cleartext_header = Some(header);
|
||||
|
||||
debug!(
|
||||
"Decrypted bulk data with user key {} of len (CT/Enc): {}/{}",
|
||||
self.user_decryption_key_uid,
|
||||
cleartext.len(),
|
||||
encrypted_block.len(),
|
||||
);
|
||||
|
||||
ser.write_vec(&cleartext)?;
|
||||
let ctx = de.read_vec_as_ref()?;
|
||||
// TODO: the encapsulation is opened each time here while its
|
||||
// bulk-encrypt counterpart associates the same encapsulation to
|
||||
// each DEM ciphertext. This incurs a significant performance
|
||||
// penalty and must be addressed. However, since the Covercrypt bulk
|
||||
// data system must be replaced by the generic one, I propose to
|
||||
// deal with both issues at once in a later time.
|
||||
let ptx = self.single_decrypt(ctx, ad, usk)?;
|
||||
ser.write_vec(&ptx)?;
|
||||
}
|
||||
|
||||
let cleartext_header = cleartext_header
|
||||
.ok_or_else(|| CryptoError::Kmip("unable to recover any header".to_owned()))?;
|
||||
|
||||
Ok((cleartext_header, ser.finalize()))
|
||||
Ok(ser.finalize())
|
||||
}
|
||||
}
|
||||
|
||||
impl DecryptionSystem for CovercryptDecryption {
|
||||
fn decrypt(&self, request: &Decrypt) -> Result<DecryptResponse, CryptoError> {
|
||||
let user_decryption_key = UserSecretKey::deserialize(&self.user_decryption_key_bytes)
|
||||
.map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"cover crypt decipher: failed recovering the user key: {e}"
|
||||
))
|
||||
})?;
|
||||
let usk = UserSecretKey::deserialize(&self.usk_bytes).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"cover crypt decrypt: failed recovering the user key: {e}"
|
||||
))
|
||||
})?;
|
||||
|
||||
let encrypted_bytes = request.data.as_ref().ok_or_else(|| {
|
||||
let ctx = request.data.as_ref().ok_or_else(|| {
|
||||
CryptoError::Kmip("The decryption request should contain encrypted data".to_owned())
|
||||
})?;
|
||||
|
||||
let (header, plaintext) = if let Some(CryptographicParameters {
|
||||
let ptx = if let Some(CryptographicParameters {
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCryptBulk),
|
||||
..
|
||||
}) = request.cryptographic_parameters
|
||||
{
|
||||
self.bulk_decrypt(
|
||||
encrypted_bytes.as_slice(),
|
||||
ctx.as_slice(),
|
||||
request.authenticated_encryption_additional_data.as_deref(),
|
||||
&user_decryption_key,
|
||||
&usk,
|
||||
)?
|
||||
} else {
|
||||
self.decrypt(
|
||||
encrypted_bytes.as_slice(),
|
||||
self.single_decrypt(
|
||||
ctx.as_slice(),
|
||||
request.authenticated_encryption_additional_data.as_deref(),
|
||||
&user_decryption_key,
|
||||
&usk,
|
||||
)?
|
||||
};
|
||||
|
||||
// Declaring a vector and then zeroizing it is fine since it represents
|
||||
// a unique pointer to data on the heap.
|
||||
let decrypted_data: Vec<u8> = DecryptedData {
|
||||
metadata: header.metadata.unwrap_or_default(),
|
||||
plaintext,
|
||||
}
|
||||
.try_into()?;
|
||||
|
||||
Ok(DecryptResponse {
|
||||
unique_identifier: UniqueIdentifier::TextString(self.user_decryption_key_uid.clone()),
|
||||
data: Some(Zeroizing::from(decrypted_data)),
|
||||
unique_identifier: UniqueIdentifier::TextString(self.usk_uid.clone()),
|
||||
data: Some(ptx),
|
||||
correlation_value: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn aead_decrypt(
|
||||
key: &SymmetricKey<{ Aes256Gcm::KEY_LENGTH }>,
|
||||
ctx: &[u8],
|
||||
ad: Option<&[u8]>,
|
||||
) -> CryptoResult<Zeroizing<Vec<u8>>> {
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
if ctx.len() < Aes256Gcm::NONCE_LENGTH {
|
||||
return Err(CryptoError::Default("encrypted block too short".to_owned()));
|
||||
}
|
||||
let nonce = Nonce::try_from_slice(&ctx[..Aes256Gcm::NONCE_LENGTH])?;
|
||||
Aes256Gcm::new(key)
|
||||
.decrypt(&nonce, &ctx[Aes256Gcm::NONCE_LENGTH..], ad)
|
||||
.map(Zeroizing::new)
|
||||
.map_err(CryptoError::from)
|
||||
}
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
use cloudproof::reexport::{
|
||||
cover_crypt::{
|
||||
abe_policy::{AccessPolicy, Policy},
|
||||
core::SYM_KEY_LENGTH,
|
||||
Covercrypt, EncryptedHeader, MasterPublicKey,
|
||||
},
|
||||
crypto_core::{
|
||||
bytes_ser_de::{Deserializer, Serializable, Serializer},
|
||||
reexport::zeroize::Zeroizing,
|
||||
SymmetricKey,
|
||||
},
|
||||
use cosmian_cover_crypt::{api::Covercrypt, traits::KemAc, AccessPolicy, MasterPublicKey};
|
||||
use cosmian_crypto_core::{
|
||||
bytes_ser_de::{Deserializer, Serializable, Serializer},
|
||||
reexport::zeroize::Zeroizing,
|
||||
Aes256Gcm, Dem, Instantiable, Nonce, RandomFixedSizeCBytes, SymmetricKey,
|
||||
};
|
||||
use cosmian_kmip::{
|
||||
kmip_2_1::{
|
||||
|
@ -20,18 +14,14 @@ use cosmian_kmip::{
|
|||
};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::{
|
||||
crypto::{cover_crypt::attributes::policy_from_attributes, EncryptionSystem},
|
||||
error::CryptoError,
|
||||
};
|
||||
use crate::{crypto::EncryptionSystem, error::CryptoError};
|
||||
|
||||
/// Encrypt a single block of data using an hybrid encryption mode
|
||||
/// Encrypt a single block of data using a KEM-DEM cryptosystem
|
||||
/// Cannot be used as a stream cipher
|
||||
pub struct CoverCryptEncryption {
|
||||
cover_crypt: Covercrypt,
|
||||
public_key_uid: String,
|
||||
public_key_bytes: Zeroizing<Vec<u8>>,
|
||||
policy: Policy,
|
||||
}
|
||||
|
||||
impl CoverCryptEncryption {
|
||||
|
@ -40,32 +30,21 @@ impl CoverCryptEncryption {
|
|||
public_key_uid: &str,
|
||||
public_key: &Object,
|
||||
) -> Result<Self, CryptoError> {
|
||||
let (public_key_bytes, public_key_attributes) =
|
||||
public_key.key_block()?.key_bytes_and_attributes()?;
|
||||
let (public_key_bytes, _) = public_key.key_block()?.key_bytes_and_attributes()?;
|
||||
|
||||
let policy = policy_from_attributes(public_key_attributes.ok_or_else(|| {
|
||||
CryptoError::Kmip(
|
||||
"the master public key does not have attributes with the Policy".to_owned(),
|
||||
)
|
||||
})?)?;
|
||||
|
||||
trace!(
|
||||
"Instantiated hybrid CoverCrypt encipher for public key id: {public_key_uid}, policy: \
|
||||
{policy:#?}"
|
||||
);
|
||||
trace!("Instantiated hybrid Covercrypt encipher for public key id: {public_key_uid}");
|
||||
|
||||
Ok(Self {
|
||||
cover_crypt,
|
||||
public_key_uid: public_key_uid.into(),
|
||||
public_key_bytes,
|
||||
policy,
|
||||
})
|
||||
}
|
||||
|
||||
/// Encrypt multiple LEB128-serialized payloads
|
||||
///
|
||||
/// The input plaintext data is serialized using LEB128 (bulk mode).
|
||||
/// Each chunk of data is encrypted and serialized back to LEB128.
|
||||
/// The input plaintext data is serialized using LEB128 (bulk mode). Each
|
||||
/// chunk of data is encrypted and serialized back to LEB128.
|
||||
///
|
||||
/// Bulk encryption / decryption scheme
|
||||
///
|
||||
|
@ -88,15 +67,20 @@ impl CoverCryptEncryption {
|
|||
///
|
||||
fn bulk_encrypt(
|
||||
&self,
|
||||
encrypted_header: &[u8],
|
||||
plaintext: &[u8],
|
||||
aead: Option<&[u8]>,
|
||||
symmetric_key: &SymmetricKey<SYM_KEY_LENGTH>,
|
||||
mpk: &MasterPublicKey,
|
||||
ptx: &[u8],
|
||||
ad: Option<&[u8]>,
|
||||
ap: &AccessPolicy,
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
let mut de = Deserializer::new(plaintext);
|
||||
let mut de = Deserializer::new(ptx);
|
||||
let mut ser = Serializer::new();
|
||||
|
||||
// number of chunks of plaintext data to encrypt
|
||||
let (seed, enc) = self.cover_crypt.encaps(mpk, ap)?;
|
||||
let key = SymmetricKey::derive(&seed, b"Covercrypt AEAD key")?;
|
||||
let enc = enc.serialize()?;
|
||||
|
||||
trace!("CoverCryptEncryption: encrypt: encryption_policy: {ap:?}",);
|
||||
|
||||
let nb_chunks = {
|
||||
let len = de.read_leb128_u64()?;
|
||||
ser.write_leb128_u64(len)?;
|
||||
|
@ -107,111 +91,93 @@ impl CoverCryptEncryption {
|
|||
})?
|
||||
};
|
||||
|
||||
// encrypt each chunk and serialize it
|
||||
// a copy of the encrypted header is also serialized, prepending the chunk
|
||||
// Encrypt each chunk and serialize it along with a copy of the
|
||||
// Covecrypt encapsulation.
|
||||
for _ in 0..nb_chunks {
|
||||
let chunk_data = de.read_vec_as_ref()?;
|
||||
let mut encrypted_block = self.encrypt(chunk_data, aead, symmetric_key)?;
|
||||
let mut chunk = encrypted_header.to_vec();
|
||||
chunk.append(&mut encrypted_block);
|
||||
ser.write_vec(&chunk)?;
|
||||
let ptx = de.read_vec_as_ref()?;
|
||||
let ctx = self.aead_encrypt(&key, ptx, ad)?;
|
||||
let res = [&**enc, &ctx].concat();
|
||||
ser.write_vec(&res)?;
|
||||
}
|
||||
|
||||
Ok(ser.finalize().to_vec())
|
||||
}
|
||||
|
||||
fn encrypt(
|
||||
fn single_encrypt(
|
||||
&self,
|
||||
plaintext: &[u8],
|
||||
aead: Option<&[u8]>,
|
||||
symmetric_key: &SymmetricKey<SYM_KEY_LENGTH>,
|
||||
mpk: &MasterPublicKey,
|
||||
ptx: &[u8],
|
||||
ad: Option<&[u8]>,
|
||||
ap: &AccessPolicy,
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
// Encrypt the data
|
||||
let encrypted_block = self
|
||||
.cover_crypt
|
||||
.encrypt(symmetric_key, plaintext, aead)
|
||||
.map_err(|e| CryptoError::Kmip(e.to_string()))?;
|
||||
let (seed, enc) = self.cover_crypt.encaps(mpk, ap)?;
|
||||
let key = SymmetricKey::derive(&seed, b"Covercrypt AEAD key")?;
|
||||
let enc = enc.serialize()?;
|
||||
trace!("CoverCryptEncryption: encrypt: encryption_policy: {ap:?}",);
|
||||
let ctx = self.aead_encrypt(&key, ptx, ad)?;
|
||||
Ok([&**enc, &ctx].concat())
|
||||
}
|
||||
|
||||
fn aead_encrypt(
|
||||
&self,
|
||||
key: &SymmetricKey<{ Aes256Gcm::KEY_LENGTH }>,
|
||||
ptx: &[u8],
|
||||
ad: Option<&[u8]>,
|
||||
) -> Result<Vec<u8>, CryptoError> {
|
||||
let nonce = Nonce::new(&mut *self.cover_crypt.rng());
|
||||
let ctx = Aes256Gcm::new(key).encrypt(&nonce, ptx, ad)?;
|
||||
let res = [&nonce.0, &*ctx].concat();
|
||||
debug!(
|
||||
"Encrypted data with public key {} of len (CT/Enc): {}/{}",
|
||||
self.public_key_uid,
|
||||
plaintext.len(),
|
||||
encrypted_block.len(),
|
||||
"Encrypted data with auth data {:?} of len (Plain/Enc): {}/{}",
|
||||
ad,
|
||||
ptx.len(),
|
||||
res.len(),
|
||||
);
|
||||
|
||||
Ok(encrypted_block)
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl EncryptionSystem for CoverCryptEncryption {
|
||||
fn encrypt(&self, request: &Encrypt) -> Result<EncryptResponse, CryptoError> {
|
||||
let authenticated_encryption_additional_data =
|
||||
request.authenticated_encryption_additional_data.as_deref();
|
||||
let ad = request.authenticated_encryption_additional_data.as_deref();
|
||||
|
||||
let data_to_encrypt = DataToEncrypt::try_from_bytes(
|
||||
trace!("CoverCryptEncryption: encrypt: authenticated_encryption_additional_data: {ad:?}",);
|
||||
|
||||
let encrypted_data =
|
||||
request
|
||||
.data
|
||||
.as_deref()
|
||||
.ok_or_else(|| CryptoError::Kmip("Missing data to encrypt".to_owned()))?,
|
||||
)?;
|
||||
.map(|data| -> Result<_, _> {
|
||||
let ptx = DataToEncrypt::try_from_bytes(data)?;
|
||||
let mpk = MasterPublicKey::deserialize(self.public_key_bytes.as_slice())
|
||||
.map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"Covercrypt encrypt: failed recovering the master public key: {e}"
|
||||
))
|
||||
})?;
|
||||
|
||||
let public_key =
|
||||
MasterPublicKey::deserialize(self.public_key_bytes.as_slice()).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"cover crypt encipher: failed recovering the public key: {e}"
|
||||
))
|
||||
})?;
|
||||
let ap = AccessPolicy::parse(ptx.encryption_policy.as_deref().ok_or_else(
|
||||
|| CryptoError::Kmip("missing encryption policy".to_owned()),
|
||||
)?)?;
|
||||
|
||||
let encryption_policy_string = data_to_encrypt
|
||||
.encryption_policy
|
||||
.as_deref()
|
||||
.ok_or_else(|| CryptoError::Kmip("encryption policy missing".to_owned()))?;
|
||||
let encryption_policy = AccessPolicy::from_boolean_expression(encryption_policy_string)
|
||||
.map_err(|e| CryptoError::Kmip(format!("invalid encryption policy: {e}")))?;
|
||||
|
||||
// Generate a symmetric key and encrypt the header
|
||||
let (symmetric_key, encrypted_header) = EncryptedHeader::generate(
|
||||
&self.cover_crypt,
|
||||
&self.policy,
|
||||
&public_key,
|
||||
&encryption_policy,
|
||||
data_to_encrypt.header_metadata.as_deref(),
|
||||
authenticated_encryption_additional_data,
|
||||
)
|
||||
.map_err(|e| CryptoError::Kmip(e.to_string()))?;
|
||||
|
||||
let mut encrypted_header = encrypted_header
|
||||
.serialize()
|
||||
.map_err(|e| CryptoError::Kmip(e.to_string()))?;
|
||||
|
||||
let encrypted_data = if let Some(CryptographicParameters {
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCryptBulk),
|
||||
..
|
||||
}) = request.cryptographic_parameters
|
||||
{
|
||||
self.bulk_encrypt(
|
||||
&encrypted_header,
|
||||
&data_to_encrypt.plaintext,
|
||||
authenticated_encryption_additional_data,
|
||||
&symmetric_key,
|
||||
)?
|
||||
} else {
|
||||
let mut encrypted_data = self.encrypt(
|
||||
&data_to_encrypt.plaintext,
|
||||
authenticated_encryption_additional_data,
|
||||
&symmetric_key,
|
||||
)?;
|
||||
encrypted_header.append(&mut encrypted_data);
|
||||
encrypted_header.to_vec()
|
||||
};
|
||||
if let Some(CryptographicParameters {
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCryptBulk),
|
||||
..
|
||||
}) = request.cryptographic_parameters
|
||||
{
|
||||
self.bulk_encrypt(&mpk, &ptx.plaintext, ad, &ap)
|
||||
} else {
|
||||
self.single_encrypt(&mpk, &ptx.plaintext, ad, &ap)
|
||||
}
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
Ok(EncryptResponse {
|
||||
unique_identifier: UniqueIdentifier::TextString(self.public_key_uid.clone()),
|
||||
data: Some(encrypted_data),
|
||||
data: encrypted_data,
|
||||
iv_counter_nonce: None,
|
||||
correlation_value: None,
|
||||
authenticated_encryption_tag: authenticated_encryption_additional_data
|
||||
.map(<[u8]>::to_vec),
|
||||
authenticated_encryption_tag: ad.map(<[u8]>::to_vec),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
use cloudproof::reexport::cover_crypt::abe_policy::Policy;
|
||||
use cosmian_cover_crypt::AccessStructure;
|
||||
use cosmian_kmip::kmip_2_1::{
|
||||
kmip_data_structures::{KeyBlock, KeyMaterial, KeyValue, KeyWrappingData},
|
||||
kmip_objects::{Object, ObjectType},
|
||||
kmip_operations::{Create, CreateKeyPair, Destroy, Import, Locate, ReKeyKeyPair},
|
||||
kmip_objects::ObjectType,
|
||||
kmip_operations::{Create, CreateKeyPair, Destroy, ReKeyKeyPair},
|
||||
kmip_types::{
|
||||
Attributes, CryptographicAlgorithm, CryptographicUsageMask, KeyFormatType, KeyWrapType,
|
||||
Link, LinkType, LinkedObjectIdentifier, UniqueIdentifier, WrappingMethod,
|
||||
Attributes, CryptographicAlgorithm, CryptographicUsageMask, KeyFormatType, Link, LinkType,
|
||||
LinkedObjectIdentifier, UniqueIdentifier,
|
||||
},
|
||||
};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use super::attributes::{
|
||||
access_policy_as_vendor_attribute, policy_as_vendor_attribute,
|
||||
access_policy_as_vendor_attribute, access_structure_as_vendor_attribute,
|
||||
rekey_edit_action_as_vendor_attribute, RekeyEditAction,
|
||||
};
|
||||
use crate::{crypto::wrap::wrap_key_bytes, error::CryptoError};
|
||||
use crate::error::CryptoError;
|
||||
|
||||
/// Build a `CreateKeyPair` request for an `CoverCrypt` Master Key
|
||||
pub fn build_create_covercrypt_master_keypair_request<T: IntoIterator<Item = impl AsRef<str>>>(
|
||||
policy: &Policy,
|
||||
access_structure: &AccessStructure,
|
||||
tags: T,
|
||||
sensitive: bool,
|
||||
) -> Result<CreateKeyPair, CryptoError> {
|
||||
|
@ -26,7 +24,9 @@ pub fn build_create_covercrypt_master_keypair_request<T: IntoIterator<Item = imp
|
|||
object_type: Some(ObjectType::PrivateKey),
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCrypt),
|
||||
key_format_type: Some(KeyFormatType::CoverCryptSecretKey),
|
||||
vendor_attributes: Some(vec![policy_as_vendor_attribute(policy)?]),
|
||||
vendor_attributes: Some(vec![access_structure_as_vendor_attribute(
|
||||
access_structure,
|
||||
)?]),
|
||||
cryptographic_usage_mask: Some(CryptographicUsageMask::Unrestricted),
|
||||
sensitive,
|
||||
..Attributes::default()
|
||||
|
@ -38,12 +38,10 @@ pub fn build_create_covercrypt_master_keypair_request<T: IntoIterator<Item = imp
|
|||
})
|
||||
}
|
||||
|
||||
/// Build a `Create` request for an `CoverCrypt` User Decryption Key
|
||||
pub fn build_create_covercrypt_user_decryption_key_request<
|
||||
T: IntoIterator<Item = impl AsRef<str>>,
|
||||
>(
|
||||
/// Build a `Create` request for a `CoverCrypt` USK
|
||||
pub fn build_create_covercrypt_usk_request<T: IntoIterator<Item = impl AsRef<str>>>(
|
||||
access_policy: &str,
|
||||
cover_crypt_master_private_key_id: &str,
|
||||
cover_crypt_master_secret_key_id: &str,
|
||||
tags: T,
|
||||
sensitive: bool,
|
||||
) -> Result<Create, CryptoError> {
|
||||
|
@ -55,7 +53,7 @@ pub fn build_create_covercrypt_user_decryption_key_request<
|
|||
link: Some(vec![Link {
|
||||
link_type: LinkType::ParentLink,
|
||||
linked_object_identifier: LinkedObjectIdentifier::TextString(
|
||||
cover_crypt_master_private_key_id.to_owned(),
|
||||
cover_crypt_master_secret_key_id.to_owned(),
|
||||
),
|
||||
}]),
|
||||
cryptographic_usage_mask: Some(CryptographicUsageMask::Unrestricted),
|
||||
|
@ -70,220 +68,6 @@ pub fn build_create_covercrypt_user_decryption_key_request<
|
|||
})
|
||||
}
|
||||
|
||||
/// Build a `Import` request for an `CoverCrypt` User Decryption Key
|
||||
///
|
||||
/// A unique identifier will be generated if none is supplied
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn build_import_decryption_private_key_request<T: IntoIterator<Item = impl AsRef<str>>>(
|
||||
private_key: &[u8],
|
||||
unique_identifier: Option<String>,
|
||||
replace_existing: bool,
|
||||
cover_crypt_master_private_key_id: &str,
|
||||
access_policy: &str,
|
||||
is_wrapped: bool,
|
||||
wrapping_password: Option<String>,
|
||||
tags: T,
|
||||
) -> Result<Import, CryptoError> {
|
||||
let mut attributes = Attributes {
|
||||
object_type: Some(ObjectType::PrivateKey),
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCrypt),
|
||||
key_format_type: Some(KeyFormatType::CoverCryptSecretKey),
|
||||
link: Some(vec![Link {
|
||||
link_type: LinkType::ParentLink,
|
||||
linked_object_identifier: LinkedObjectIdentifier::TextString(
|
||||
cover_crypt_master_private_key_id.to_owned(),
|
||||
),
|
||||
}]),
|
||||
vendor_attributes: Some(vec![access_policy_as_vendor_attribute(access_policy)?]),
|
||||
cryptographic_usage_mask: Some(CryptographicUsageMask::Unrestricted),
|
||||
..Attributes::default()
|
||||
};
|
||||
attributes.set_tags(tags)?;
|
||||
|
||||
// The key could be:
|
||||
// - already wrapped (is_wrapped is true)
|
||||
// - to wrap (wrapping_password is some)
|
||||
// - or not wrapped (otherwise)
|
||||
let is_wrapped = is_wrapped || wrapping_password.is_some();
|
||||
let key = Zeroizing::from(if let Some(wrapping_password) = wrapping_password {
|
||||
wrap_key_bytes(private_key, &[0_u8; 16], &wrapping_password)?
|
||||
} else {
|
||||
private_key.to_vec()
|
||||
});
|
||||
|
||||
let cryptographic_length = Some(i32::try_from(private_key.len())? * 8);
|
||||
Ok(Import {
|
||||
unique_identifier: UniqueIdentifier::TextString(unique_identifier.unwrap_or_default()),
|
||||
object_type: ObjectType::PrivateKey,
|
||||
replace_existing: Some(replace_existing),
|
||||
// We don't deal with the case we need to unwrapped before storing
|
||||
key_wrap_type: if is_wrapped {
|
||||
Some(KeyWrapType::AsRegistered)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
attributes: attributes.clone(),
|
||||
object: Object::PrivateKey {
|
||||
key_block: KeyBlock {
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCrypt),
|
||||
key_format_type: KeyFormatType::CoverCryptSecretKey,
|
||||
key_compression_type: None,
|
||||
key_value: KeyValue {
|
||||
key_material: KeyMaterial::ByteString(key),
|
||||
attributes: Some(attributes),
|
||||
},
|
||||
cryptographic_length,
|
||||
key_wrapping_data: if is_wrapped {
|
||||
Some(KeyWrappingData {
|
||||
wrapping_method: WrappingMethod::Encrypt,
|
||||
..KeyWrappingData::default()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Build a `Import` request for an Cover Crypt Master Private Key
|
||||
///
|
||||
/// A unique identifier will be generated if none is supplied
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn build_import_private_key_request<T: IntoIterator<Item = impl AsRef<str>>>(
|
||||
private_key: &[u8],
|
||||
unique_identifier: Option<String>,
|
||||
replace_existing: bool,
|
||||
cover_crypt_master_public_key_id: &str,
|
||||
policy: &Policy,
|
||||
is_wrapped: bool,
|
||||
wrapping_password: Option<String>,
|
||||
tags: T,
|
||||
) -> Result<Import, CryptoError> {
|
||||
let mut attributes = Attributes {
|
||||
object_type: Some(ObjectType::PrivateKey),
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCrypt),
|
||||
key_format_type: Some(KeyFormatType::CoverCryptSecretKey),
|
||||
vendor_attributes: Some(vec![policy_as_vendor_attribute(policy)?]),
|
||||
link: Some(vec![Link {
|
||||
link_type: LinkType::PublicKeyLink,
|
||||
linked_object_identifier: LinkedObjectIdentifier::TextString(
|
||||
cover_crypt_master_public_key_id.to_owned(),
|
||||
),
|
||||
}]),
|
||||
cryptographic_usage_mask: Some(CryptographicUsageMask::Unrestricted),
|
||||
..Attributes::default()
|
||||
};
|
||||
attributes.set_tags(tags)?;
|
||||
|
||||
// The key could be:
|
||||
// - already wrapped (is_wrapped is true)
|
||||
// - to wrapped (wrapping_password is some)
|
||||
// - or not wrapped (otherwise)
|
||||
let is_wrapped = is_wrapped || wrapping_password.is_some();
|
||||
let key = Zeroizing::from(if let Some(wrapping_password) = wrapping_password {
|
||||
wrap_key_bytes(private_key, &[0_u8; 16], &wrapping_password)?
|
||||
} else {
|
||||
private_key.to_vec()
|
||||
});
|
||||
|
||||
let cryptographic_length = Some(i32::try_from(private_key.len())? * 8);
|
||||
Ok(Import {
|
||||
unique_identifier: UniqueIdentifier::TextString(unique_identifier.unwrap_or_default()),
|
||||
object_type: ObjectType::PrivateKey,
|
||||
replace_existing: Some(replace_existing),
|
||||
key_wrap_type: if is_wrapped {
|
||||
Some(KeyWrapType::AsRegistered)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
attributes: attributes.clone(),
|
||||
object: Object::PrivateKey {
|
||||
key_block: KeyBlock {
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCrypt),
|
||||
key_format_type: KeyFormatType::CoverCryptSecretKey,
|
||||
key_compression_type: None,
|
||||
key_value: KeyValue {
|
||||
key_material: KeyMaterial::ByteString(key),
|
||||
attributes: Some(attributes),
|
||||
},
|
||||
cryptographic_length,
|
||||
key_wrapping_data: if is_wrapped {
|
||||
Some(KeyWrappingData {
|
||||
wrapping_method: WrappingMethod::Encrypt,
|
||||
..KeyWrappingData::default()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Build a `Import` request for an Cover Crypt Master Public Key
|
||||
///
|
||||
/// A unique identifier will be generated if none is supplied
|
||||
pub fn build_import_public_key_request<T: IntoIterator<Item = impl AsRef<str>>>(
|
||||
public_key: &[u8],
|
||||
unique_identifier: Option<String>,
|
||||
replace_existing: bool,
|
||||
policy: &Policy,
|
||||
cover_crypt_master_private_key_id: &str,
|
||||
tags: T,
|
||||
) -> Result<Import, CryptoError> {
|
||||
let mut attributes = Attributes {
|
||||
object_type: Some(ObjectType::PublicKey),
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCrypt),
|
||||
key_format_type: Some(KeyFormatType::CoverCryptSecretKey),
|
||||
vendor_attributes: Some(vec![policy_as_vendor_attribute(policy)?]),
|
||||
link: Some(vec![Link {
|
||||
link_type: LinkType::PrivateKeyLink,
|
||||
linked_object_identifier: LinkedObjectIdentifier::TextString(
|
||||
cover_crypt_master_private_key_id.to_owned(),
|
||||
),
|
||||
}]),
|
||||
..Attributes::default()
|
||||
};
|
||||
attributes.set_tags(tags)?;
|
||||
|
||||
let cryptographic_length = Some(i32::try_from(public_key.len())? * 8);
|
||||
Ok(Import {
|
||||
unique_identifier: UniqueIdentifier::TextString(unique_identifier.unwrap_or_default()),
|
||||
object_type: ObjectType::PublicKey,
|
||||
replace_existing: Some(replace_existing),
|
||||
key_wrap_type: None,
|
||||
attributes: attributes.clone(),
|
||||
object: Object::PublicKey {
|
||||
key_block: KeyBlock {
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCrypt),
|
||||
key_format_type: KeyFormatType::CoverCryptPublicKey,
|
||||
key_compression_type: None,
|
||||
key_value: KeyValue {
|
||||
key_material: KeyMaterial::ByteString(Zeroizing::from(public_key.to_vec())),
|
||||
attributes: Some(attributes),
|
||||
},
|
||||
cryptographic_length,
|
||||
key_wrapping_data: None,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Build a `Locate` request to locate an `CoverCrypt` Symmetric Key
|
||||
pub fn build_locate_symmetric_key_request(access_policy: &str) -> Result<Locate, CryptoError> {
|
||||
Ok(Locate {
|
||||
attributes: Attributes {
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::AES),
|
||||
key_format_type: Some(KeyFormatType::TransparentSymmetricKey),
|
||||
object_type: Some(ObjectType::SymmetricKey),
|
||||
vendor_attributes: Some(vec![access_policy_as_vendor_attribute(access_policy)?]),
|
||||
..Attributes::default()
|
||||
},
|
||||
..Locate::default()
|
||||
})
|
||||
}
|
||||
|
||||
/// Build a `Destroy` request to destroy an `CoverCrypt` User Decryption Key
|
||||
pub fn build_destroy_key_request(unique_identifier: &str) -> Result<Destroy, CryptoError> {
|
||||
Ok(Destroy {
|
||||
|
@ -292,21 +76,20 @@ pub fn build_destroy_key_request(unique_identifier: &str) -> Result<Destroy, Cry
|
|||
})
|
||||
}
|
||||
|
||||
/// Build a `ReKeyKeyPair` request to locate an `CoverCrypt` User Decryption Key
|
||||
/// To rekey an attribute of a user decryption key, we first need:
|
||||
/// - the master private key uid
|
||||
/// Build a `ReKeyKeyPair` request.
|
||||
/// To re-key an attribute of a user decryption key, we first need:
|
||||
/// - the MSK UID
|
||||
/// - the `CoverCrypt` attributes to revoke
|
||||
/// - the `ReKeyKeyPairAction` to perform
|
||||
///
|
||||
/// The routine will then locate and renew all user decryption keys with those `CoverCrypt` attributes
|
||||
/// The routine will then locate and renew all user decryption keys linked to
|
||||
/// this MSK.
|
||||
pub fn build_rekey_keypair_request(
|
||||
master_private_key_unique_identifier: &str,
|
||||
msk_uid: &str,
|
||||
action: &RekeyEditAction,
|
||||
) -> Result<ReKeyKeyPair, CryptoError> {
|
||||
Ok(ReKeyKeyPair {
|
||||
private_key_unique_identifier: Some(UniqueIdentifier::TextString(
|
||||
master_private_key_unique_identifier.to_owned(),
|
||||
)),
|
||||
private_key_unique_identifier: Some(UniqueIdentifier::TextString(msk_uid.to_owned())),
|
||||
private_key_attributes: Some(Attributes {
|
||||
object_type: Some(ObjectType::PrivateKey),
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCrypt),
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use cloudproof::reexport::{
|
||||
cover_crypt::{abe_policy::Policy, Covercrypt, MasterPublicKey, MasterSecretKey},
|
||||
crypto_core::bytes_ser_de::Serializable,
|
||||
};
|
||||
use cosmian_cover_crypt::{api::Covercrypt, MasterPublicKey, MasterSecretKey};
|
||||
use cosmian_crypto_core::bytes_ser_de::Serializable;
|
||||
use cosmian_kmip::kmip_2_1::{
|
||||
extra::VENDOR_ID_COSMIAN,
|
||||
kmip_data_structures::{KeyBlock, KeyMaterial, KeyValue},
|
||||
kmip_objects::{Object, ObjectType},
|
||||
kmip_types::{
|
||||
|
@ -10,11 +9,14 @@ use cosmian_kmip::kmip_2_1::{
|
|||
LinkedObjectIdentifier,
|
||||
},
|
||||
};
|
||||
use tracing::debug;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use crate::{
|
||||
crypto::{
|
||||
cover_crypt::attributes::{policy_from_attributes, upsert_policy_in_attributes},
|
||||
cover_crypt::attributes::{
|
||||
access_structure_from_attributes, VENDOR_ATTR_COVER_CRYPT_ACCESS_STRUCTURE,
|
||||
},
|
||||
KeyPair,
|
||||
},
|
||||
error::CryptoError,
|
||||
|
@ -23,97 +25,76 @@ use crate::{
|
|||
/// Group a key UID with its KMIP Object
|
||||
pub type KmipKeyUidObject = (String, Object);
|
||||
|
||||
/// Generate a `KeyPair` `(PrivateKey, MasterPublicKey)` from the attributes
|
||||
/// of a `CreateKeyPair` operation
|
||||
/// Generate a new Covercrypt master keypair the attributes of a `CreateKeyPair`
|
||||
/// operation.
|
||||
pub fn create_master_keypair(
|
||||
cover_crypt: &Covercrypt,
|
||||
private_key_uid: &str,
|
||||
private_key_uid: String,
|
||||
public_key_uid: &str,
|
||||
common_attributes: &Option<Attributes>,
|
||||
private_key_attributes: &Option<Attributes>,
|
||||
public_key_attributes: &Option<Attributes>,
|
||||
mut common_attributes: Attributes,
|
||||
msk_attributes: Option<Attributes>,
|
||||
mpk_attributes: Option<Attributes>,
|
||||
sensitive: bool,
|
||||
) -> Result<KeyPair, CryptoError> {
|
||||
let any_attributes = common_attributes
|
||||
.as_ref()
|
||||
.or(private_key_attributes.as_ref())
|
||||
.or(public_key_attributes.as_ref())
|
||||
.ok_or_else(|| {
|
||||
CryptoError::Kmip("Attributes must be provided in a CreateKeyPair request".to_owned())
|
||||
})?;
|
||||
let access_structure = access_structure_from_attributes(&common_attributes)?;
|
||||
|
||||
// verify that we can recover the policy
|
||||
let policy = policy_from_attributes(any_attributes)?;
|
||||
debug!("server: access_structure: {access_structure:?}");
|
||||
|
||||
// Now generate a master key using the CoverCrypt Engine
|
||||
let (sk, pk) = cover_crypt
|
||||
.generate_master_keys(&policy)
|
||||
let (mut msk, _) = cover_crypt
|
||||
.setup()
|
||||
.map_err(|e| CryptoError::Kmip(e.to_string()))?;
|
||||
msk.access_structure = access_structure;
|
||||
let mpk = cover_crypt.update_msk(&mut msk)?;
|
||||
|
||||
// Private Key generation
|
||||
// First generate fresh attributes with that policy
|
||||
let private_key_attributes = private_key_attributes
|
||||
.as_ref()
|
||||
.or(common_attributes.as_ref());
|
||||
let sk_bytes = sk.serialize().map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"cover crypt: failed serializing the master private key: {e}"
|
||||
))
|
||||
})?;
|
||||
let private_key = create_master_private_key_object(
|
||||
&sk_bytes,
|
||||
&policy,
|
||||
private_key_attributes,
|
||||
// Removes the access structure from the common attributes.
|
||||
common_attributes
|
||||
.remove_vendor_attribute(VENDOR_ID_COSMIAN, VENDOR_ATTR_COVER_CRYPT_ACCESS_STRUCTURE);
|
||||
|
||||
let msk_owm = create_msk_object(
|
||||
msk.serialize()?,
|
||||
msk_attributes.unwrap_or_else(|| common_attributes.clone()),
|
||||
public_key_uid,
|
||||
sensitive,
|
||||
)?;
|
||||
|
||||
// Public Key generation
|
||||
// First generate fresh attributes with that policy
|
||||
let public_key_attributes = public_key_attributes
|
||||
.as_ref()
|
||||
.or(common_attributes.as_ref());
|
||||
let pk_bytes = pk.serialize().map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"cover crypt: failed serializing the master public key: {e}"
|
||||
))
|
||||
})?;
|
||||
let public_key = create_master_public_key_object(
|
||||
&pk_bytes,
|
||||
&policy,
|
||||
public_key_attributes,
|
||||
let mpk_owm = create_mpk_object(
|
||||
mpk.serialize()?,
|
||||
mpk_attributes.unwrap_or(common_attributes),
|
||||
private_key_uid,
|
||||
)?;
|
||||
|
||||
Ok(KeyPair((private_key, public_key)))
|
||||
Ok(KeyPair((msk_owm, mpk_owm)))
|
||||
}
|
||||
|
||||
fn create_master_private_key_object(
|
||||
key: &[u8],
|
||||
policy: &Policy,
|
||||
attributes: Option<&Attributes>,
|
||||
master_public_key_uid: &str,
|
||||
pub fn create_msk_object(
|
||||
msk_bytes: Zeroizing<Vec<u8>>,
|
||||
mut attributes: Attributes,
|
||||
mpk_uid: &str,
|
||||
sensitive: bool,
|
||||
) -> Result<Object, CryptoError> {
|
||||
let mut attributes = attributes.cloned().unwrap_or_default();
|
||||
debug!(
|
||||
"create_msk_object: key len: {}, attributes: {attributes:?}",
|
||||
msk_bytes.len()
|
||||
);
|
||||
|
||||
attributes.object_type = Some(ObjectType::PrivateKey);
|
||||
attributes.key_format_type = Some(KeyFormatType::CoverCryptSecretKey);
|
||||
// Covercrypt keys are set to have unrestricted usage.
|
||||
attributes.set_cryptographic_usage_mask_bits(CryptographicUsageMask::Unrestricted);
|
||||
// add the policy to the attributes
|
||||
upsert_policy_in_attributes(&mut attributes, policy)?;
|
||||
// link the private key to the public key
|
||||
attributes.link = Some(vec![Link {
|
||||
link_type: LinkType::PublicKeyLink,
|
||||
linked_object_identifier: LinkedObjectIdentifier::TextString(
|
||||
master_public_key_uid.to_owned(),
|
||||
),
|
||||
linked_object_identifier: LinkedObjectIdentifier::TextString(mpk_uid.to_owned()),
|
||||
}]);
|
||||
let cryptographic_length = Some(i32::try_from(key.len())? * 8);
|
||||
attributes.sensitive = sensitive;
|
||||
|
||||
let cryptographic_length = Some(i32::try_from(msk_bytes.len())? * 8);
|
||||
|
||||
Ok(Object::PrivateKey {
|
||||
key_block: KeyBlock {
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCrypt),
|
||||
key_format_type: KeyFormatType::CoverCryptSecretKey,
|
||||
key_compression_type: None,
|
||||
key_value: KeyValue {
|
||||
key_material: KeyMaterial::ByteString(Zeroizing::from(key.to_vec())),
|
||||
key_material: KeyMaterial::ByteString(msk_bytes),
|
||||
attributes: Some(attributes),
|
||||
},
|
||||
cryptographic_length,
|
||||
|
@ -126,26 +107,20 @@ fn create_master_private_key_object(
|
|||
/// Policy and optional additional attributes
|
||||
///
|
||||
/// see `cover_crypt_unwrap_master_public_key` for the reverse operation
|
||||
fn create_master_public_key_object(
|
||||
key: &[u8],
|
||||
policy: &Policy,
|
||||
attributes: Option<&Attributes>,
|
||||
master_private_key_uid: &str,
|
||||
fn create_mpk_object(
|
||||
key: Zeroizing<Vec<u8>>,
|
||||
mut attributes: Attributes,
|
||||
msk_uid: String,
|
||||
) -> Result<Object, CryptoError> {
|
||||
let mut attributes = attributes.cloned().unwrap_or_default();
|
||||
attributes.sensitive = false;
|
||||
attributes.object_type = Some(ObjectType::PublicKey);
|
||||
attributes.key_format_type = Some(KeyFormatType::CoverCryptPublicKey);
|
||||
// Covercrypt keys are set to have unrestricted usage.
|
||||
attributes.set_cryptographic_usage_mask_bits(CryptographicUsageMask::Unrestricted);
|
||||
// add the policy to the attributes
|
||||
upsert_policy_in_attributes(&mut attributes, policy)?;
|
||||
// link the public key to the private key
|
||||
attributes.link = Some(vec![Link {
|
||||
link_type: LinkType::PrivateKeyLink,
|
||||
linked_object_identifier: LinkedObjectIdentifier::TextString(
|
||||
master_private_key_uid.to_owned(),
|
||||
),
|
||||
linked_object_identifier: LinkedObjectIdentifier::TextString(msk_uid),
|
||||
}]);
|
||||
let cryptographic_length = Some(i32::try_from(key.len())? * 8);
|
||||
Ok(Object::PublicKey {
|
||||
|
@ -154,7 +129,7 @@ fn create_master_public_key_object(
|
|||
key_format_type: KeyFormatType::CoverCryptPublicKey,
|
||||
key_compression_type: None,
|
||||
key_value: KeyValue {
|
||||
key_material: KeyMaterial::ByteString(Zeroizing::from(key.to_vec())),
|
||||
key_material: KeyMaterial::ByteString(key),
|
||||
attributes: Some(attributes),
|
||||
},
|
||||
cryptographic_length,
|
||||
|
@ -163,63 +138,54 @@ fn create_master_public_key_object(
|
|||
})
|
||||
}
|
||||
|
||||
pub fn covercrypt_keys_from_kmip_objects(
|
||||
master_private_key: &Object,
|
||||
master_public_key: &Object,
|
||||
pub fn cc_master_keypair_from_kmip_objects(
|
||||
msk: &Object,
|
||||
mpk: &Object,
|
||||
) -> Result<(MasterSecretKey, MasterPublicKey), CryptoError> {
|
||||
// Recover the CoverCrypt PrivateKey Object
|
||||
let msk_key_block = master_private_key.key_block()?;
|
||||
let msk_key_bytes = msk_key_block.key_bytes()?;
|
||||
let msk = MasterSecretKey::deserialize(&msk_key_bytes).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"Failed deserializing the CoverCrypt Master Private Key: {e}"
|
||||
))
|
||||
})?;
|
||||
let msk_bytes = msk.key_block()?.key_bytes()?;
|
||||
let msk = MasterSecretKey::deserialize(&msk_bytes)
|
||||
.map_err(|e| CryptoError::Kmip(format!("Failed deserializing the Covercrypt MSK: {e}")))?;
|
||||
|
||||
// Recover the CoverCrypt MasterPublicKey Object
|
||||
let mpk_key_block = master_public_key.key_block()?;
|
||||
let mpk_key_bytes = mpk_key_block.key_bytes()?;
|
||||
let mpk = MasterPublicKey::deserialize(&mpk_key_bytes).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"Failed deserializing the CoverCrypt Master Public Key: {e}"
|
||||
))
|
||||
})?;
|
||||
let mpk_bytes = mpk.key_block()?.key_bytes()?;
|
||||
let mpk = MasterPublicKey::deserialize(&mpk_bytes)
|
||||
.map_err(|e| CryptoError::Kmip(format!("Failed deserializing the Covercrypt MPK: {e}")))?;
|
||||
|
||||
Ok((msk, mpk))
|
||||
}
|
||||
|
||||
pub fn kmip_objects_from_covercrypt_keys(
|
||||
policy: &Policy,
|
||||
pub fn kmip_objects_from_cc_master_keypair(
|
||||
msk: &MasterSecretKey,
|
||||
mpk: &MasterPublicKey,
|
||||
msk_obj: KmipKeyUidObject,
|
||||
mpk_obj: KmipKeyUidObject,
|
||||
) -> Result<(KmipKeyUidObject, KmipKeyUidObject), CryptoError> {
|
||||
let updated_master_private_key_bytes = &msk.serialize().map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"Failed serializing the CoverCrypt Master Private Key: {e}"
|
||||
))
|
||||
})?;
|
||||
let updated_master_private_key = create_master_private_key_object(
|
||||
updated_master_private_key_bytes,
|
||||
policy,
|
||||
Some(msk_obj.1.attributes()?),
|
||||
&mpk_obj.0,
|
||||
)?;
|
||||
let updated_master_public_key_bytes = &mpk.serialize().map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"Failed serializing the CoverCrypt Master Public Key: {e}"
|
||||
))
|
||||
})?;
|
||||
let updated_master_public_key = create_master_public_key_object(
|
||||
updated_master_public_key_bytes,
|
||||
policy,
|
||||
Some(mpk_obj.1.attributes()?),
|
||||
&msk_obj.0,
|
||||
)?;
|
||||
mut msk_obj: Object,
|
||||
mut mpk_obj: Object,
|
||||
) -> Result<(Object, Object), CryptoError> {
|
||||
let msk_bytes = msk
|
||||
.serialize()
|
||||
.map_err(|e| CryptoError::Kmip(format!("Failed serializing the Covercrypt MSK: {e}")))?;
|
||||
|
||||
Ok((
|
||||
(msk_obj.0, updated_master_private_key),
|
||||
(mpk_obj.0, updated_master_public_key),
|
||||
))
|
||||
match &mut msk_obj.key_block_mut()?.key_value.key_material {
|
||||
KeyMaterial::ByteString(bytes) => {
|
||||
*bytes = msk_bytes;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(CryptoError::Kmip(
|
||||
"wrong key material type for MSK".to_owned(),
|
||||
)),
|
||||
}?;
|
||||
|
||||
let mpk_bytes = mpk
|
||||
.serialize()
|
||||
.map_err(|e| CryptoError::Kmip(format!("Failed serializing the Covercrypt MPK: {e}")))?;
|
||||
|
||||
match &mut mpk_obj.key_block_mut()?.key_value.key_material {
|
||||
KeyMaterial::ByteString(bytes) => {
|
||||
*bytes = mpk_bytes;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(CryptoError::Kmip(
|
||||
"wrong key material type for MPK".to_owned(),
|
||||
)),
|
||||
}?;
|
||||
|
||||
Ok((msk_obj, mpk_obj))
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
pub mod access_structure;
|
||||
pub mod attributes;
|
||||
pub mod decryption;
|
||||
pub mod encryption;
|
||||
pub mod kmip_requests;
|
||||
pub mod master_keys;
|
||||
pub mod secret_key;
|
||||
pub mod user_key;
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
use std::convert::TryFrom;
|
||||
|
||||
use cloudproof::reexport::{
|
||||
cover_crypt::{abe_policy::AccessPolicy, Covercrypt, MasterPublicKey},
|
||||
crypto_core::bytes_ser_de::Serializable,
|
||||
};
|
||||
use cosmian_kmip::kmip_2_1::{
|
||||
kmip_data_structures::{KeyBlock, KeyMaterial, KeyValue, KeyWrappingData},
|
||||
kmip_objects::{Object, ObjectType},
|
||||
kmip_operations::GetResponse,
|
||||
kmip_types::{
|
||||
Attributes, CryptographicAlgorithm, CryptographicUsageMask, KeyFormatType, WrappingMethod,
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{debug, trace};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use crate::{
|
||||
crypto::cover_crypt::attributes::{access_policy_as_vendor_attribute, policy_from_attributes},
|
||||
error::CryptoError,
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------- setup parameters for KMIP --------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/// The whole Key Value structure is wrapped
|
||||
/// A reference to the `CoverCrypt` master public key is kept to access the policy later
|
||||
/// when locating symmetric keys
|
||||
pub fn wrapped_secret_key(
|
||||
cover_crypt: &Covercrypt,
|
||||
public_key_response: &GetResponse,
|
||||
access_policy: &str,
|
||||
cover_crypt_header_uid: &[u8],
|
||||
) -> Result<Object, CryptoError> {
|
||||
let sk = prepare_symmetric_key(
|
||||
cover_crypt,
|
||||
public_key_response,
|
||||
&AccessPolicy::from_boolean_expression(access_policy)?,
|
||||
cover_crypt_header_uid,
|
||||
)?;
|
||||
// Since KMIP 2.1 does not plan to locate wrapped key, we serialize vendor
|
||||
// attributes and symmetric key consecutively
|
||||
let wrapped_key_attributes = Attributes {
|
||||
object_type: Some(ObjectType::SymmetricKey),
|
||||
vendor_attributes: Some(vec![access_policy_as_vendor_attribute(access_policy)?]),
|
||||
cryptographic_usage_mask: Some(CryptographicUsageMask::Unrestricted),
|
||||
..Attributes::default()
|
||||
};
|
||||
|
||||
let cryptographic_length = Some(i32::try_from(sk.encrypted_symmetric_key.len())? * 8);
|
||||
let key_value = KeyValue {
|
||||
key_material: KeyMaterial::ByteString(Zeroizing::from(sk.encrypted_symmetric_key)),
|
||||
attributes: Some(wrapped_key_attributes),
|
||||
};
|
||||
let key_wrapping_data = KeyWrappingData {
|
||||
wrapping_method: WrappingMethod::Encrypt,
|
||||
iv_counter_nonce: None,
|
||||
..KeyWrappingData::default()
|
||||
};
|
||||
|
||||
Ok(Object::SymmetricKey {
|
||||
key_block: KeyBlock {
|
||||
cryptographic_algorithm: Some(CryptographicAlgorithm::AES),
|
||||
key_format_type: KeyFormatType::TransparentSymmetricKey,
|
||||
key_compression_type: None,
|
||||
key_value,
|
||||
cryptographic_length,
|
||||
key_wrapping_data: Some(key_wrapping_data),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct CoverCryptSymmetricKey {
|
||||
pub symmetric_key: Vec<u8>,
|
||||
pub uid: Vec<u8>,
|
||||
pub encrypted_symmetric_key: Vec<u8>,
|
||||
}
|
||||
|
||||
fn prepare_symmetric_key(
|
||||
cover_crypt: &Covercrypt,
|
||||
public_key_response: &GetResponse,
|
||||
access_policy: &AccessPolicy,
|
||||
cover_crypt_header_uid: &[u8],
|
||||
) -> Result<CoverCryptSymmetricKey, CryptoError> {
|
||||
trace!("Starting create secret key");
|
||||
|
||||
let (public_key_bytes, public_key_attributes) = public_key_response
|
||||
.object
|
||||
.key_block()?
|
||||
.key_bytes_and_attributes()?;
|
||||
|
||||
let public_key = MasterPublicKey::deserialize(&public_key_bytes).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"cover crypt: failed deserializing the master public key: {e}"
|
||||
))
|
||||
})?;
|
||||
|
||||
let policy = policy_from_attributes(public_key_attributes.ok_or_else(|| {
|
||||
CryptoError::Kmip(
|
||||
"the master public key does not have attributes with the Policy".to_owned(),
|
||||
)
|
||||
})?)?;
|
||||
|
||||
let (sk, sk_enc) = cover_crypt
|
||||
.encaps(&policy, &public_key, access_policy)
|
||||
.map_err(|e| CryptoError::Kmip(e.to_string()))?;
|
||||
|
||||
debug!("Generate symmetric key for CoverCrypt OK");
|
||||
Ok(CoverCryptSymmetricKey {
|
||||
uid: cover_crypt_header_uid.to_vec(),
|
||||
symmetric_key: sk.to_vec(),
|
||||
encrypted_symmetric_key: sk_enc
|
||||
.serialize()
|
||||
.map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"cover crypt: failed serializing the encapsulation: {e}"
|
||||
))
|
||||
})?
|
||||
.to_vec(),
|
||||
})
|
||||
}
|
||||
|
||||
impl TryFrom<&KeyBlock> for CoverCryptSymmetricKey {
|
||||
type Error = CryptoError;
|
||||
|
||||
fn try_from(sk: &KeyBlock) -> Result<Self, Self::Error> {
|
||||
if sk.cryptographic_algorithm != Some(CryptographicAlgorithm::CoverCrypt)
|
||||
|| sk.key_format_type != KeyFormatType::TransparentSymmetricKey
|
||||
{
|
||||
return Err(CryptoError::Kmip(
|
||||
"this Secret Key does not contain an CoverCrypt key".to_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
if sk.key_wrapping_data.is_some() {
|
||||
return Err(CryptoError::Kmip(
|
||||
"unwrapping an CoverCrypt Secret Key is not yet supported".to_owned(),
|
||||
))
|
||||
}
|
||||
serde_json::from_slice::<Self>(match &sk.key_value.key_material {
|
||||
KeyMaterial::TransparentSymmetricKey { key } => key,
|
||||
other => {
|
||||
return Err(CryptoError::Kmip(format!(
|
||||
"Invalid key material for an CoverCrypt secret key: {other}"
|
||||
)))
|
||||
}
|
||||
})
|
||||
.map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"failed deserializing the CoverCrypt Secret Key from the Key Material {e}"
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,10 +1,5 @@
|
|||
use cloudproof::reexport::{
|
||||
cover_crypt::{
|
||||
abe_policy::{AccessPolicy, Policy},
|
||||
Covercrypt, MasterSecretKey, UserSecretKey,
|
||||
},
|
||||
crypto_core::bytes_ser_de::Serializable,
|
||||
};
|
||||
use cosmian_cover_crypt::{api::Covercrypt, AccessPolicy, MasterSecretKey, UserSecretKey};
|
||||
use cosmian_crypto_core::bytes_ser_de::Serializable;
|
||||
use cosmian_kmip::kmip_2_1::{
|
||||
kmip_data_structures::{KeyBlock, KeyMaterial, KeyValue},
|
||||
kmip_objects::{Object, ObjectType},
|
||||
|
@ -17,8 +12,7 @@ use tracing::trace;
|
|||
use zeroize::Zeroizing;
|
||||
|
||||
use crate::{
|
||||
crypto::cover_crypt::attributes::{policy_from_attributes, upsert_access_policy_in_attributes},
|
||||
error::CryptoError,
|
||||
crypto::cover_crypt::attributes::upsert_access_policy_in_attributes, error::CryptoError,
|
||||
};
|
||||
|
||||
/// Unwrap the User Decryption Key bytes, Policy and Access Policy from the
|
||||
|
@ -34,20 +28,20 @@ pub(crate) fn unwrap_user_decryption_key_object(
|
|||
};
|
||||
if key_block.key_format_type != KeyFormatType::CoverCryptSecretKey {
|
||||
return Err(CryptoError::Kmip(
|
||||
"Expected an CoverCrypt User Decryption Key".to_owned(),
|
||||
"Expected an Covercrypt User Decryption Key".to_owned(),
|
||||
))
|
||||
}
|
||||
let bytes = match &key_block.key_value.key_material {
|
||||
KeyMaterial::ByteString(b) => b.clone(),
|
||||
x => {
|
||||
return Err(CryptoError::Kmip(format!(
|
||||
"Invalid Key Material for the CoverCrypt User Decryption Key: {x}"
|
||||
"Invalid Key Material for the Covercrypt User Decryption Key: {x}"
|
||||
)))
|
||||
}
|
||||
};
|
||||
let attributes = key_block.key_value.attributes().map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"The CoverCrypt Master private key should have attributes: {e}"
|
||||
"The Covercrypt User Decryption Key should have attributes: {e}"
|
||||
))
|
||||
})?;
|
||||
Ok((bytes, attributes.clone()))
|
||||
|
@ -55,55 +49,39 @@ pub(crate) fn unwrap_user_decryption_key_object(
|
|||
|
||||
/// Handles operations on user keys, caching the engine
|
||||
/// and the master key information for efficiency
|
||||
pub struct UserDecryptionKeysHandler {
|
||||
cover_crypt: Covercrypt,
|
||||
master_private_key: MasterSecretKey,
|
||||
policy: Policy,
|
||||
pub struct UserDecryptionKeysHandler<'a> {
|
||||
cover_crypt: &'a Covercrypt,
|
||||
msk: &'a mut MasterSecretKey,
|
||||
}
|
||||
|
||||
impl UserDecryptionKeysHandler {
|
||||
pub fn instantiate(
|
||||
cover_crypt: Covercrypt,
|
||||
master_private_key: &Object,
|
||||
) -> Result<Self, CryptoError> {
|
||||
let msk_key_block = master_private_key.key_block()?;
|
||||
let msk_key_bytes = msk_key_block.key_bytes()?;
|
||||
let msk = MasterSecretKey::deserialize(&msk_key_bytes).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"cover crypt: failed deserializing the master private key: {e}"
|
||||
))
|
||||
})?;
|
||||
let private_key_attributes = master_private_key.attributes()?;
|
||||
let policy = policy_from_attributes(private_key_attributes)?;
|
||||
Ok(Self {
|
||||
cover_crypt,
|
||||
master_private_key: msk,
|
||||
policy,
|
||||
})
|
||||
impl<'a> UserDecryptionKeysHandler<'a> {
|
||||
pub fn instantiate(cover_crypt: &'a Covercrypt, msk: &'a mut MasterSecretKey) -> Self {
|
||||
Self { cover_crypt, msk }
|
||||
}
|
||||
|
||||
/// Create a User Decryption Key Object from the passed master private key bytes,
|
||||
/// Policy, Access Policy and optional additional attributes
|
||||
/// Create a User Decryption Key Object from the passed master secret key bytes,
|
||||
/// Access Policy and optional additional attributes
|
||||
///
|
||||
/// see `cover_crypt_unwrap_user_decryption_key` for the reverse operation
|
||||
pub fn create_user_decryption_key_object(
|
||||
&self,
|
||||
pub fn create_usk_object(
|
||||
&mut self,
|
||||
access_policy_str: &str,
|
||||
attributes: Option<&Attributes>,
|
||||
master_private_key_id: &str,
|
||||
msk_id: &str,
|
||||
) -> Result<Object, CryptoError> {
|
||||
//
|
||||
// Generate a fresh user decryption key
|
||||
//
|
||||
let access_policy = AccessPolicy::from_boolean_expression(access_policy_str)?;
|
||||
|
||||
let access_policy = AccessPolicy::parse(access_policy_str)?;
|
||||
trace!("create_user_decryption_key_object: Access Policy: {access_policy:?}");
|
||||
let uk = self
|
||||
.cover_crypt
|
||||
.generate_user_secret_key(&self.master_private_key, &access_policy, &self.policy)
|
||||
.generate_user_secret_key(self.msk, &access_policy)
|
||||
.map_err(|e| CryptoError::Kmip(e.to_string()))?;
|
||||
trace!("Created user decryption key {uk:?} with access policy: {access_policy:?}");
|
||||
|
||||
trace!("Created user decryption key with access policy: {access_policy:?}");
|
||||
let user_decryption_key_bytes = uk.serialize().map_err(|e| {
|
||||
CryptoError::Kmip(format!("cover crypt: failed serializing the user key: {e}"))
|
||||
CryptoError::Kmip(format!("covercrypt: failed serializing the user key: {e}"))
|
||||
})?;
|
||||
let user_decryption_key_len = user_decryption_key_bytes.len();
|
||||
|
||||
|
@ -115,12 +93,10 @@ impl UserDecryptionKeysHandler {
|
|||
// Add the access policy to the attributes
|
||||
upsert_access_policy_in_attributes(&mut attributes, access_policy_str)?;
|
||||
|
||||
// Add the link to the master private key
|
||||
// Add the link to the master secret key
|
||||
attributes.link = Some(vec![Link {
|
||||
link_type: LinkType::ParentLink,
|
||||
linked_object_identifier: LinkedObjectIdentifier::TextString(
|
||||
master_private_key_id.to_owned(),
|
||||
),
|
||||
linked_object_identifier: LinkedObjectIdentifier::TextString(msk_id.to_owned()),
|
||||
}]);
|
||||
let cryptographic_length = Some(i32::try_from(user_decryption_key_len)? * 8);
|
||||
Ok(Object::PrivateKey {
|
||||
|
@ -139,8 +115,8 @@ impl UserDecryptionKeysHandler {
|
|||
}
|
||||
|
||||
/// Refresh the user decryption key according to the (new) policy of the master key
|
||||
pub fn refresh_user_decryption_key_object(
|
||||
&self,
|
||||
pub fn refresh_usk_object(
|
||||
&mut self,
|
||||
user_decryption_key: &Object,
|
||||
keep_old_rights: bool,
|
||||
) -> Result<Object, CryptoError> {
|
||||
|
@ -148,22 +124,22 @@ impl UserDecryptionKeysHandler {
|
|||
unwrap_user_decryption_key_object(user_decryption_key)?;
|
||||
let mut usk = UserSecretKey::deserialize(&usk_key_bytes).map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"cover crypt: failed deserializing the user decryption key: {e}"
|
||||
"covercrypt: failed deserializing the user decryption key: {e}"
|
||||
))
|
||||
})?;
|
||||
|
||||
self.cover_crypt
|
||||
.refresh_user_secret_key(&mut usk, &self.master_private_key, keep_old_rights)
|
||||
.refresh_usk(self.msk, &mut usk, keep_old_rights)
|
||||
.map_err(|e| {
|
||||
CryptoError::Kmip(format!(
|
||||
"cover crypt: failed refreshing the user decryption key: {e}"
|
||||
"covercrypt: failed refreshing the user decryption key: {e}"
|
||||
))
|
||||
})?;
|
||||
|
||||
trace!("Refreshed user decryption key {usk:?}");
|
||||
|
||||
let user_decryption_key_bytes = usk.serialize().map_err(|e| {
|
||||
CryptoError::Kmip(format!("cover crypt: failed serializing the user key: {e}"))
|
||||
CryptoError::Kmip(format!("covercrypt: failed serializing the user key: {e}"))
|
||||
})?;
|
||||
let cryptographic_length = Some(i32::try_from(user_decryption_key_bytes.len())? * 8);
|
||||
Ok(Object::PrivateKey {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use cloudproof::reexport::crypto_core::{
|
||||
use cosmian_crypto_core::{
|
||||
reexport::rand_core::SeedableRng, CsRng, Ecies, EciesSalsaSealBox, Ed25519PrivateKey,
|
||||
Ed25519PublicKey, X25519PrivateKey, X25519PublicKey,
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ const AES_BLOCK_SIZE: usize = 0x10;
|
|||
///
|
||||
/// `wrapped_key_size` is the size of the key to wrap.
|
||||
fn build_iv(wrapped_key_size: usize) -> CryptoResult<u64> {
|
||||
Ok(u64::from(DEFAULT_RFC5649_CONST) << 32 | u64::try_from(wrapped_key_size)?.to_le())
|
||||
Ok((u64::from(DEFAULT_RFC5649_CONST) << 32) | u64::try_from(wrapped_key_size)?.to_le())
|
||||
}
|
||||
|
||||
/// Check if the `iv` value obtained after decryption is appropriate according
|
||||
|
@ -88,7 +88,7 @@ pub fn rfc5649_wrap(plain: &[u8], kek: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|||
|
||||
Ok(ciphertext[..AES_BLOCK_SIZE].to_vec())
|
||||
} else {
|
||||
_wrap_64(&padded_plain, kek, Some(build_iv(n)?))
|
||||
wrap_64(&padded_plain, kek, Some(build_iv(n)?))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ pub fn rfc5649_unwrap(ciphertext: &[u8], kek: &[u8]) -> Result<Zeroizing<Vec<u8>
|
|||
}
|
||||
|
||||
if n > 16 {
|
||||
let (iv, padded_plain) = _unwrap_64(ciphertext, kek)?;
|
||||
let (iv, padded_plain) = unwrap_64(ciphertext, kek)?;
|
||||
|
||||
// Verify integrity check register as described in RFC 5649.
|
||||
if !check_iv(iv, &padded_plain)? {
|
||||
|
@ -165,7 +165,7 @@ pub fn rfc5649_unwrap(ciphertext: &[u8], kek: &[u8]) -> Result<Zeroizing<Vec<u8>
|
|||
///
|
||||
/// The function name matches the one used in the RFC and has no link to the
|
||||
/// unwrap function in Rust.
|
||||
fn _wrap_64(plain: &[u8], kek: &[u8], iv: Option<u64>) -> Result<Vec<u8>, CryptoError> {
|
||||
fn wrap_64(plain: &[u8], kek: &[u8], iv: Option<u64>) -> Result<Vec<u8>, CryptoError> {
|
||||
let n = plain.len();
|
||||
|
||||
if n % AES_WRAP_PAD_BLOCK_SIZE != 0 {
|
||||
|
@ -198,7 +198,7 @@ fn _wrap_64(plain: &[u8], kek: &[u8], iv: Option<u64>) -> Result<Vec<u8>, Crypto
|
|||
for j in 0..6 {
|
||||
for (i, block) in blocks.iter_mut().enumerate().take(n) {
|
||||
// B = AES(K, A | R[i])
|
||||
let plaintext_block = (u128::from(icr) << 64 | u128::from(*block)).to_be_bytes();
|
||||
let plaintext_block = ((u128::from(icr) << 64) | u128::from(*block)).to_be_bytes();
|
||||
|
||||
/*
|
||||
* Encrypt block using AES with ECB mode i.e. raw AES as specified in
|
||||
|
@ -223,7 +223,7 @@ fn _wrap_64(plain: &[u8], kek: &[u8], iv: Option<u64>) -> Result<Vec<u8>, Crypto
|
|||
Ok(wrapped_key)
|
||||
}
|
||||
|
||||
fn _unwrap_64(ciphertext: &[u8], kek: &[u8]) -> Result<(u64, Zeroizing<Vec<u8>>), CryptoError> {
|
||||
fn unwrap_64(ciphertext: &[u8], kek: &[u8]) -> Result<(u64, Zeroizing<Vec<u8>>), CryptoError> {
|
||||
let n = ciphertext.len();
|
||||
|
||||
if n % AES_WRAP_PAD_BLOCK_SIZE != 0 || n < AES_BLOCK_SIZE {
|
||||
|
@ -266,7 +266,7 @@ fn _unwrap_64(ciphertext: &[u8], kek: &[u8]) -> Result<(u64, Zeroizing<Vec<u8>>)
|
|||
let t = u64::try_from((n * j) + (n - i))?;
|
||||
|
||||
// B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i
|
||||
let big_i = (u128::from(icr ^ t) << 64 | u128::from(*block)).to_be_bytes();
|
||||
let big_i = ((u128::from(icr ^ t) << 64) | u128::from(*block)).to_be_bytes();
|
||||
let big_b = big_i.as_slice();
|
||||
|
||||
let mut plaintext = Zeroizing::from(vec![0; big_b.len() + AES_BLOCK_SIZE]);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::num::TryFromIntError;
|
||||
|
||||
use cloudproof::reexport::crypto_core::{reexport::pkcs8, CryptoCoreError};
|
||||
use cosmian_crypto_core::CryptoCoreError;
|
||||
use cosmian_kmip::KmipError;
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -26,6 +26,9 @@ pub enum CryptoError {
|
|||
#[error("Invalid tag: {0}")]
|
||||
InvalidTag(String),
|
||||
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error("KMIP Error: {0}")]
|
||||
Kmip(String),
|
||||
|
||||
|
@ -43,6 +46,9 @@ pub enum CryptoError {
|
|||
|
||||
#[error(transparent)]
|
||||
TryFromSliceError(#[from] std::array::TryFromSliceError),
|
||||
|
||||
#[error(transparent)]
|
||||
Covercrypt(#[from] cosmian_cover_crypt::Error),
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for CryptoError {
|
||||
|
@ -51,49 +57,18 @@ impl From<Vec<u8>> for CryptoError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<cloudproof::reexport::cover_crypt::Error> for CryptoError {
|
||||
fn from(e: cloudproof::reexport::cover_crypt::Error) -> Self {
|
||||
Self::Default(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CryptoCoreError> for CryptoError {
|
||||
fn from(e: CryptoCoreError) -> Self {
|
||||
Self::Default(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pyo3")]
|
||||
impl From<pyo3::PyErr> for CryptoError {
|
||||
fn from(e: pyo3::PyErr) -> Self {
|
||||
Self::Default(e.to_string())
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "pyo3")]
|
||||
impl From<CryptoError> for pyo3::PyErr {
|
||||
fn from(e: CryptoError) -> Self {
|
||||
pyo3::exceptions::PyException::new_err(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<openssl::error::ErrorStack> for CryptoError {
|
||||
fn from(e: openssl::error::ErrorStack) -> Self {
|
||||
Self::OpenSSL(format!("Error: {e}. Details: {e:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<pkcs8::spki::Error> for CryptoError {
|
||||
fn from(e: pkcs8::spki::Error) -> Self {
|
||||
Self::ConversionError(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<pkcs8::Error> for CryptoError {
|
||||
fn from(e: pkcs8::Error) -> Self {
|
||||
Self::ConversionError(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TryFromIntError> for CryptoError {
|
||||
fn from(e: TryFromIntError) -> Self {
|
||||
Self::ConversionError(e.to_string())
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
// clippy::use_debug,
|
||||
)]
|
||||
#![allow(
|
||||
clippy::too_long_first_doc_paragraph,
|
||||
clippy::module_name_repetitions,
|
||||
clippy::similar_names,
|
||||
clippy::too_many_lines,
|
||||
|
@ -52,8 +53,6 @@
|
|||
)]
|
||||
// required to detect generic type in Serializer
|
||||
#![feature(min_specialization)]
|
||||
// To parse a slice
|
||||
#![feature(slice_take)]
|
||||
|
||||
pub use error::{result::CryptoResultHelper, CryptoError};
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ rust-version.workspace = true
|
|||
doctest = false
|
||||
|
||||
[features]
|
||||
pyo3 = ["dep:pyo3"]
|
||||
# Enable FIPS module feature build. KMS builds in FIPS mode when this is enabled.
|
||||
fips = []
|
||||
|
||||
|
@ -29,7 +28,6 @@ num-bigint-dig = { workspace = true, features = [
|
|||
"serde",
|
||||
"zeroize",
|
||||
] }
|
||||
pyo3 = { workspace = true, optional = true }
|
||||
serde = { workspace = true, features = ["derive", "std"] }
|
||||
serde_json = { workspace = true, features = [] }
|
||||
strum = { workspace = true, features = ["std", "derive", "strum_macros"] }
|
||||
|
|
|
@ -2,25 +2,20 @@ use crate::{error::KmipError, kmip_2_1::kmip_operations::ErrorReason, Deserializ
|
|||
|
||||
/// Structure used to encrypt with Covercrypt or ECIES
|
||||
///
|
||||
/// To encrypt some data with Covercrypt we need to
|
||||
/// pass an access policy. The KMIP format do not provide
|
||||
/// us a way to send this access policy with the plaintext
|
||||
/// data to encrypt (in a vendor attribute for example).
|
||||
/// We need to prepend the encoded access policy to the plaintext
|
||||
/// bytes and decode them in the KMS code before encrypting with
|
||||
/// Covercrypt. This struct is not useful (and shouldn't be use)
|
||||
/// if the user ask to encrypt with something else than Cover Crypt
|
||||
/// (for example an AES encrypt.) See also `DecryptedData` struct.
|
||||
/// The binary format of this struct is:
|
||||
/// 1. LEB128 unsigned length of encryption policy string in UTF8 encoded bytes
|
||||
/// 2. encryption policy string in UTF8 encoded bytes
|
||||
/// 3. LEB128 unsigned length of additional metadata
|
||||
/// 4. additional metadata encrypted in the header by the DEM
|
||||
/// 5. plaintext data to encrypt
|
||||
/// To encrypt some data with Covercrypt we need to pass an access policy. The
|
||||
/// KMIP format do not provide us a way to send this access policy with the
|
||||
/// plaintext data to encrypt (in a vendor attribute for example). We need to
|
||||
/// prepend the encoded access policy to the plaintext bytes and decode them in
|
||||
/// the KMS code before encrypting with Covercrypt. This struct is not useful
|
||||
/// (and shouldn't be use) if the user ask to encrypt with something else than
|
||||
/// Cover Crypt (for example an AES encrypt.) See also `DecryptedData` struct.
|
||||
/// The binary format of this struct is: 1. LEB128 unsigned length of encryption
|
||||
/// policy string in UTF8 encoded bytes 2. encryption policy string in UTF8
|
||||
/// encoded bytes 3. LEB128 unsigned length of additional metadata 4. additional
|
||||
/// metadata encrypted in the header by the DEM 5. plaintext data to encrypt
|
||||
#[derive(Debug, PartialEq, Eq, Default)]
|
||||
pub struct DataToEncrypt {
|
||||
pub encryption_policy: Option<String>,
|
||||
pub header_metadata: Option<Vec<u8>>,
|
||||
pub plaintext: Vec<u8>,
|
||||
}
|
||||
|
||||
|
@ -34,9 +29,6 @@ impl DataToEncrypt {
|
|||
if let Some(encryption_policy) = &self.encryption_policy {
|
||||
mem_size += encryption_policy.len();
|
||||
}
|
||||
if let Some(metadata) = &self.header_metadata {
|
||||
mem_size += metadata.len();
|
||||
}
|
||||
|
||||
// Write the encryption policy
|
||||
let mut se = Serializer::with_capacity(mem_size);
|
||||
|
@ -45,12 +37,6 @@ impl DataToEncrypt {
|
|||
} else {
|
||||
se.write_leb128_u64(0)?;
|
||||
}
|
||||
// Write the metadata
|
||||
if let Some(metadata) = &self.header_metadata {
|
||||
se.write_vec(metadata)?;
|
||||
} else {
|
||||
se.write_leb128_u64(0)?;
|
||||
}
|
||||
// Write the plaintext
|
||||
let mut bytes = se.finalize().to_vec();
|
||||
bytes.extend_from_slice(&self.plaintext);
|
||||
|
@ -74,17 +60,11 @@ impl DataToEncrypt {
|
|||
})
|
||||
.transpose()?;
|
||||
|
||||
// Read the metadata
|
||||
let metadata = de
|
||||
.read_vec()
|
||||
.map(|metadata| (!metadata.is_empty()).then_some(metadata))?;
|
||||
|
||||
// Remaining is the plaintext to encrypt
|
||||
let plaintext = de.finalize();
|
||||
|
||||
Ok(Self {
|
||||
encryption_policy,
|
||||
header_metadata: metadata,
|
||||
plaintext,
|
||||
})
|
||||
}
|
||||
|
@ -101,7 +81,6 @@ mod tests {
|
|||
{
|
||||
let data_to_encrypt = DataToEncrypt {
|
||||
encryption_policy: Some("a && b".to_owned()),
|
||||
header_metadata: Some(String::from("äbcdef").into_bytes()),
|
||||
plaintext: String::from("this is a plain text à è ").into_bytes(),
|
||||
};
|
||||
let bytes = data_to_encrypt.to_bytes().unwrap();
|
||||
|
@ -112,7 +91,6 @@ mod tests {
|
|||
{
|
||||
let data_to_encrypt = DataToEncrypt {
|
||||
encryption_policy: Some("a && b".to_owned()),
|
||||
header_metadata: None,
|
||||
plaintext: String::from("this is a plain text à è ").into_bytes(),
|
||||
};
|
||||
let bytes = data_to_encrypt.to_bytes().unwrap();
|
||||
|
@ -123,7 +101,6 @@ mod tests {
|
|||
{
|
||||
let data_to_encrypt = DataToEncrypt {
|
||||
encryption_policy: None,
|
||||
header_metadata: Some(String::from("äbcdef").into_bytes()),
|
||||
plaintext: String::from("this is a plain text à è ").into_bytes(),
|
||||
};
|
||||
let bytes = data_to_encrypt.to_bytes().unwrap();
|
||||
|
@ -134,7 +111,6 @@ mod tests {
|
|||
{
|
||||
let data_to_encrypt = DataToEncrypt {
|
||||
encryption_policy: None,
|
||||
header_metadata: None,
|
||||
plaintext: String::from("this is a plain text à è ").into_bytes(),
|
||||
};
|
||||
let bytes = data_to_encrypt.to_bytes().unwrap();
|
||||
|
|
|
@ -82,19 +82,6 @@ impl From<TtlvError> for KmipError {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pyo3")]
|
||||
impl From<pyo3::PyErr> for KmipError {
|
||||
fn from(e: pyo3::PyErr) -> Self {
|
||||
Self::Kmip(ErrorReason::Codec_Error, e.to_string())
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "pyo3")]
|
||||
impl From<KmipError> for pyo3::PyErr {
|
||||
fn from(e: KmipError) -> Self {
|
||||
pyo3::exceptions::PyException::new_err(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TryFromIntError> for KmipError {
|
||||
fn from(e: TryFromIntError) -> Self {
|
||||
Self::ConversionError(e.to_string())
|
||||
|
|
|
@ -227,6 +227,9 @@ impl Display for KeyValue {
|
|||
}
|
||||
}
|
||||
|
||||
// This is required since its signature must match what serde
|
||||
// skip_serializing_if is expecting.
|
||||
#[allow(clippy::ref_option)]
|
||||
// Attributes is default is a fix for https://github.com/Cosmian/kms/issues/92
|
||||
fn attributes_is_default_or_none<T: Default + PartialEq + Serialize>(val: &Option<T>) -> bool {
|
||||
val.as_ref().map_or(true, |v| *v == T::default())
|
||||
|
|
|
@ -240,6 +240,11 @@ impl Object {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the `Attributes` of that object if any, an error otherwise
|
||||
pub fn into_attributes(&self) -> Result<&Attributes, KmipError> {
|
||||
self.key_block()?.attributes()
|
||||
}
|
||||
|
||||
/// Returns the `Attributes` of that object if any, an error otherwise
|
||||
pub fn attributes(&self) -> Result<&Attributes, KmipError> {
|
||||
self.key_block()?.attributes()
|
||||
|
|
|
@ -20,7 +20,6 @@ use super::{
|
|||
UniqueIdentifier, ValidityIndicator,
|
||||
},
|
||||
};
|
||||
use crate::{error::KmipError, Deserializer, Serializer};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Display, Debug, Eq, PartialEq, Default)]
|
||||
|
@ -1305,7 +1304,7 @@ impl Display for Encrypt {
|
|||
self.correlation_value,
|
||||
self.init_indicator,
|
||||
self.final_indicator,
|
||||
self.authenticated_encryption_additional_data
|
||||
self.authenticated_encryption_additional_data,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1439,55 +1438,6 @@ impl Display for Decrypt {
|
|||
}
|
||||
}
|
||||
|
||||
/// When decrypting data with Cover Crypt we can have some
|
||||
/// additional metadata stored inside the header and encrypted
|
||||
/// with de DEM. We need to return these data to the user but
|
||||
/// the KMIP protocol do not provide a way to do it. So we prepend
|
||||
/// the decrypted bytes with the decrypted additional metadata.
|
||||
/// This struct is not useful (and shouldn't be use) if the user
|
||||
/// ask to encrypt with something else than Cover Crypt (for example an AES encrypt.)
|
||||
/// See also `DataToEncrypt` struct.
|
||||
/// The binary format of this struct is:
|
||||
/// 1. LEB128 unsigned length of the metadata
|
||||
/// 2. metadata decrypted bytes
|
||||
/// 3. data decrypted
|
||||
pub struct DecryptedData {
|
||||
pub metadata: Vec<u8>,
|
||||
pub plaintext: Zeroizing<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl TryInto<Vec<u8>> for DecryptedData {
|
||||
type Error = KmipError;
|
||||
|
||||
fn try_into(self) -> Result<Vec<u8>, Self::Error> {
|
||||
let mut ser = Serializer::new();
|
||||
ser.write_vec(&self.metadata)?;
|
||||
|
||||
let mut result = ser.finalize().to_vec();
|
||||
result.extend_from_slice(&self.plaintext);
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for DecryptedData {
|
||||
type Error = KmipError;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
let mut de = Deserializer::new(bytes);
|
||||
|
||||
// Read the metadata
|
||||
let metadata = de.read_vec()?;
|
||||
|
||||
// Remaining is the decrypted plaintext
|
||||
let plaintext = Zeroizing::from(de.finalize());
|
||||
|
||||
Ok(Self {
|
||||
metadata,
|
||||
plaintext,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct DecryptResponse {
|
||||
|
@ -1871,9 +1821,7 @@ impl Display for ReKeyKeyPair {
|
|||
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct ReKeyKeyPairResponse {
|
||||
// The Unique Identifier of the newly created replacement Private Key object.
|
||||
pub private_key_unique_identifier: UniqueIdentifier,
|
||||
// The Unique Identifier of the newly created replacement Public Key object.}
|
||||
pub public_key_unique_identifier: UniqueIdentifier,
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,7 @@
|
|||
|
||||
// see CryptographicUsageMask
|
||||
#![allow(non_upper_case_globals)]
|
||||
use std::{
|
||||
fmt,
|
||||
fmt::{Display, Formatter},
|
||||
};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use serde::{
|
||||
de::{self, MapAccess, Visitor},
|
||||
|
|
|
@ -78,7 +78,7 @@ impl FromStr for KmipOperation {
|
|||
"locate" => Ok(Self::Locate),
|
||||
"rekey" => Ok(Self::Rekey),
|
||||
"revoke" => Ok(Self::Revoke),
|
||||
_ => Err("Could not parse an operation {op}"),
|
||||
_ => Err("Could not parse an operation"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ pub fn decrypt_request(
|
|||
nonce: Option<Vec<u8>>,
|
||||
ciphertext: Vec<u8>,
|
||||
authenticated_tag: Option<Vec<u8>>,
|
||||
authentication_data: Option<Vec<u8>>,
|
||||
authenticated_encryption_additional_data: Option<Vec<u8>>,
|
||||
cryptographic_parameters: Option<CryptographicParameters>,
|
||||
) -> Decrypt {
|
||||
Decrypt {
|
||||
|
@ -26,7 +26,7 @@ pub fn decrypt_request(
|
|||
correlation_value: None,
|
||||
init_indicator: None,
|
||||
final_indicator: None,
|
||||
authenticated_encryption_additional_data: authentication_data,
|
||||
authenticated_encryption_additional_data,
|
||||
authenticated_encryption_tag: authenticated_tag,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
DataToEncrypt, KmipError,
|
||||
};
|
||||
|
||||
/// Build an Encryption Request to encrypt the provided `plaintext`
|
||||
/// Build an Encryption Request to encrypt the provided `plaintext`.
|
||||
/// The cryptographic scheme is determined by that of the key identified by `key_unique_identifier`
|
||||
/// For Covercrypt,
|
||||
/// - the `encryption_policy` must be provided
|
||||
|
@ -20,32 +20,30 @@ pub fn encrypt_request(
|
|||
key_unique_identifier: &str,
|
||||
encryption_policy: Option<String>,
|
||||
plaintext: Vec<u8>,
|
||||
header_metadata: Option<Vec<u8>>,
|
||||
nonce: Option<Vec<u8>>,
|
||||
authentication_data: Option<Vec<u8>>,
|
||||
authenticated_encryption_additional_data: Option<Vec<u8>>,
|
||||
cryptographic_parameters: Option<CryptographicParameters>,
|
||||
) -> Result<Encrypt, KmipError> {
|
||||
let data_to_encrypt = Zeroizing::from(if encryption_policy.is_some() {
|
||||
let data_to_encrypt = if encryption_policy.is_some() {
|
||||
DataToEncrypt {
|
||||
encryption_policy,
|
||||
header_metadata,
|
||||
plaintext,
|
||||
}
|
||||
.to_bytes()?
|
||||
} else {
|
||||
plaintext
|
||||
});
|
||||
};
|
||||
|
||||
Ok(Encrypt {
|
||||
unique_identifier: Some(UniqueIdentifier::TextString(
|
||||
key_unique_identifier.to_owned(),
|
||||
)),
|
||||
cryptographic_parameters,
|
||||
data: Some(data_to_encrypt),
|
||||
data: Some(Zeroizing::from(data_to_encrypt)),
|
||||
iv_counter_nonce: nonce,
|
||||
correlation_value: None,
|
||||
init_indicator: None,
|
||||
final_indicator: None,
|
||||
authenticated_encryption_additional_data: authentication_data,
|
||||
authenticated_encryption_additional_data,
|
||||
})
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue