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:
Manuthor 2025-03-21 18:23:59 +01:00 committed by GitHub
parent 5614124311
commit 5a826b465d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
184 changed files with 2100 additions and 5800 deletions

View file

@ -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

View file

@ -10,6 +10,8 @@ on:
debug_or_release:
required: true
type: string
workflow_dispatch:
jobs:
rhel9:

View file

@ -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' }}

View file

@ -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 }}

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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
View file

@ -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",
]

View file

@ -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 }

View file

@ -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.**

View file

@ -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 }

View file

@ -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)

View file

@ -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 {

View file

@ -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() {

View file

@ -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

View file

@ -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 {

View file

@ -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
}
}

View file

@ -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()
};

View file

@ -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(())

View file

@ -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,

View file

@ -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
}
}

View file

@ -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;

View 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(())
}
}

View file

@ -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(),
)?;

View file

@ -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()),

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -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
}
}

View file

@ -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()
}
}

View file

@ -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
}
}

View file

@ -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?,

View file

@ -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}"))
);
}
}

View file

@ -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(

View file

@ -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()),

View file

@ -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
}

View file

@ -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
}
}

View 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";

View file

@ -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;

View file

@ -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(

View file

@ -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),

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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(),

View 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)
}

View file

@ -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());

View file

@ -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.

View file

@ -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;

View file

@ -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

View file

@ -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),

View file

@ -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
}
}

View file

@ -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,
}

View file

@ -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
}
}

View file

@ -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}")]

View file

@ -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,

View file

@ -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 {

View file

@ -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?;

View file

@ -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,

View file

@ -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(())
}

View file

@ -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;

View file

@ -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"),
)

View file

@ -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(())
}

View file

@ -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()),

View file

@ -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,
)?;

View file

@ -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(),

View file

@ -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,
)?;

View file

@ -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,
)?;

View file

@ -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,
)?;

View file

@ -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,
)?;

View file

@ -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,
)?;

View file

@ -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,
};

View file

@ -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()),

View file

@ -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" }

View file

@ -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())
}
}

View file

@ -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::{

View file

@ -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,

View file

@ -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"]

View 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)
}

View file

@ -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}"
))
})
},

View file

@ -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)
}

View file

@ -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),
})
}
}

View file

@ -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),

View file

@ -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))
}

View file

@ -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;

View file

@ -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}"
))
})
}
}

View file

@ -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 {

View file

@ -1,4 +1,4 @@
use cloudproof::reexport::crypto_core::{
use cosmian_crypto_core::{
reexport::rand_core::SeedableRng, CsRng, Ecies, EciesSalsaSealBox, Ed25519PrivateKey,
Ed25519PublicKey, X25519PrivateKey, X25519PublicKey,
};

View file

@ -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]);

View file

@ -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())

View file

@ -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};

View file

@ -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"] }

View file

@ -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();

View file

@ -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())

View file

@ -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())

View file

@ -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()

View file

@ -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,
}

View file

@ -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},

View file

@ -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"),
}
}
}

View file

@ -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,
}
}

View file

@ -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