├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .dockerignore ├── .github ├── actions │ └── build_containers │ │ └── action.yml ├── commitlint.config.mjs ├── dependabot.yml ├── labeler.yml ├── spellcheck-ignore └── workflows │ ├── analysis.yml │ ├── cd.yml │ ├── ci.yml │ ├── integration.yaml │ └── labeler.yml ├── .gitignore ├── .gitleaks.toml ├── .mergify.yml ├── .packit.yaml ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── HOWTO.md ├── LICENSE ├── Makefile ├── README.md ├── RELEASING.md ├── TESTING.md ├── admin-tool ├── Cargo.toml └── src │ ├── aio │ ├── configure.rs │ ├── device.rs │ ├── execute.rs │ └── mod.rs │ └── main.rs ├── bundled-provides.jq ├── client-linuxapp ├── Cargo.toml └── src │ ├── main.rs │ ├── reencrypt │ ├── mod.rs │ └── rebind.rs │ └── serviceinfo.rs ├── contrib └── containers │ ├── README.md │ ├── admin-cli │ ├── aio │ ├── build │ ├── client-linuxapp │ ├── manufacturing-server │ ├── owner-onboarding-server │ ├── rendezvous-server │ └── serviceinfo-api-server ├── data-formats ├── Cargo.toml ├── build.rs └── src │ ├── cborparser.rs │ ├── constants │ ├── mod.rs │ └── serviceinfo_names.rs │ ├── devicecredential │ ├── file.rs │ └── mod.rs │ ├── enhanced_types.rs │ ├── errors.rs │ ├── lib.rs │ ├── messages │ ├── mod.rs │ ├── v10 │ │ ├── di.rs │ │ ├── diun.rs │ │ ├── error.rs │ │ ├── mod.rs │ │ ├── to0.rs │ │ ├── to1.rs │ │ └── to2.rs │ └── v11 │ │ ├── di.rs │ │ ├── diun.rs │ │ ├── error.rs │ │ ├── mod.rs │ │ ├── to0.rs │ │ ├── to1.rs │ │ └── to2.rs │ ├── ownershipvoucher.rs │ ├── publickey.rs │ ├── serializable.rs │ └── types.rs ├── db ├── Cargo.toml └── src │ ├── lib.rs │ ├── models.rs │ ├── postgres.rs │ ├── schema.rs │ └── sqlite.rs ├── docs-rpms ├── fdo-admin-cli.rst ├── fdo-client.rst ├── fdo-init.rst ├── fdo-manufacturing-server.rst ├── fdo-owner-cli.rst ├── fdo-owner-onboarding-server.rst └── fdo-rendezvous-server.rst ├── docs ├── Gemfile ├── _config.yml ├── index.md ├── sims │ ├── encryption.md │ └── index.md └── specs │ ├── diun.md │ ├── index.md │ ├── ov_management_api.md │ └── serviceinfo_api.md ├── dracut └── 52fdo │ ├── manufacturing-client-generator │ ├── manufacturing-client-service │ ├── manufacturing-client.service │ └── module-setup.sh ├── examples ├── config │ ├── device_specific_serviceinfo.yml │ ├── manufacturing-server.yml │ ├── owner-onboarding-server.yml │ ├── rendezvous-info.yml │ ├── rendezvous-server.yml │ └── serviceinfo-api-server.yml └── systemd │ ├── fdo-aio.service │ ├── fdo-client-linuxapp.service │ ├── fdo-manufacturing-server.service │ ├── fdo-owner-onboarding-server.service │ ├── fdo-rendezvous-server.service │ └── fdo-serviceinfo-api-server.service ├── fido-device-onboard.spec ├── http-wrapper ├── Cargo.toml └── src │ ├── client.rs │ ├── lib.rs │ └── server.rs ├── integration-tests ├── Cargo.toml ├── locator.rs ├── templates │ ├── manufacturing-server.yml.j2 │ ├── owner-onboarding-server.yml.j2 │ ├── rendezvous-info.yml.j2 │ ├── rendezvous-server.yml.j2 │ └── serviceinfo-api-server.yml.j2 ├── tests │ ├── common │ │ └── mod.rs │ ├── di_diun.rs │ ├── e2e.rs │ ├── service_info.rs │ ├── to.rs │ └── voucher_tests.rs ├── util_bins │ ├── clevis-decrypt-test │ └── clevis-encrypt-test └── vouchers │ ├── v100 │ ├── voucher1 │ └── voucher2 │ └── v101 │ ├── voucher1 │ ├── voucher2 │ └── voucher3 ├── konflux ├── README.md ├── fdo-container-test.yaml ├── fdo-manufacturing-server-integration-tests.yaml ├── fdo-owner-onboarding-server-integration-tests.yaml ├── fdo-rendezvous-server-integration-tests.yaml └── fdo-serviceinfo-api-server-integration-tests.yaml ├── libfdo-data-go ├── cimport.go ├── errors.go ├── go.mod ├── local.go ├── ownershipvoucher.go └── strings.go ├── libfdo-data ├── Cargo.toml ├── build.rs ├── fdo_data.h ├── libfdo-data-go.doc ├── src │ ├── lib.rs │ ├── ownershipvoucher.rs │ └── test_common.rs ├── test_assets │ └── testdevice1.ov └── test_scripts │ ├── go.mod │ ├── ownershipvoucher.go │ └── ownershipvoucher_many.go ├── manufacturing-client ├── Cargo.toml └── src │ └── main.rs ├── manufacturing-server ├── Cargo.toml └── src │ ├── handlers │ ├── di.rs │ ├── diun.rs │ └── mod.rs │ └── main.rs ├── migrations ├── migrations_manufacturing_server_postgres │ └── 2023-10-03-152801_create_db │ │ ├── down.sql │ │ └── up.sql ├── migrations_manufacturing_server_sqlite │ └── 2023-10-03-152801_create_db │ │ ├── down.sql │ │ └── up.sql ├── migrations_owner_onboarding_server_postgres │ └── 2023-10-03-152801_create_db │ │ ├── down.sql │ │ └── up.sql ├── migrations_owner_onboarding_server_sqlite │ └── 2023-10-03-152801_create_db │ │ ├── down.sql │ │ └── up.sql ├── migrations_rendezvous_server_postgres │ └── 2023-10-03-152801_create_db │ │ ├── down.sql │ │ └── up.sql └── migrations_rendezvous_server_sqlite │ └── 2023-10-03-152801_create_db │ ├── down.sql │ └── up.sql ├── owner-onboarding-server ├── Cargo.toml └── src │ ├── handlers.rs │ ├── main.rs │ └── serviceinfo │ └── mod.rs ├── owner-tool ├── Cargo.toml └── src │ └── main.rs ├── patches └── 0001-use-released-aws-nitro-enclaves-cose-version.patch ├── rendezvous-server ├── Cargo.toml └── src │ ├── handlers_to0.rs │ ├── handlers_to1.rs │ └── main.rs ├── serviceinfo-api-server ├── Cargo.toml └── src │ └── main.rs ├── store ├── Cargo.toml └── src │ ├── directory.rs │ ├── lib.rs │ ├── pg.rs │ └── sqlite.rs ├── test ├── ansible.cfg ├── check-ostree.yaml ├── fdo-container.sh ├── fdo-postgres.sh ├── fdo │ ├── manufacturing-server.yml │ ├── owner-onboarding-server.yml │ ├── rendezvous-server.yml │ └── serviceinfo-api-server.yml ├── files │ ├── centos-stream-9.json │ └── clients ├── fmf │ ├── .fmf │ │ └── version │ ├── plans │ │ └── onboarding.fmf │ └── tests │ │ └── onboarding │ │ ├── main.fmf │ │ └── run-onboarding.sh └── setup.sh └── util ├── Cargo.toml └── src ├── device_credential_locations.rs ├── device_identification.rs ├── lib.rs ├── passwd_shadow.rs ├── servers ├── configuration │ ├── manufacturing_server.rs │ ├── mod.rs │ ├── owner_onboarding_server.rs │ ├── rendezvous_server.rs │ └── serviceinfo_api_server.rs └── mod.rs └── system_info.rs /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fedora:latest 2 | 3 | ENV PATH "$PATH:/home/vscode/.cargo/bin" 4 | 5 | RUN bash -c "$(curl -fsSL "https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/script-library/common-redhat.sh")" -- "true" "vscode" "1000" "1000" "true" 6 | 7 | RUN dnf install -y \ 8 | sudo git cargo rust rust-src git-core openssl openssl-devel clippy rustfmt golang tpm2-tss-devel clevis clevis-luks cryptsetup cryptsetup-devel clang-devel sqlite sqlite-devel libpq libpq-devel \ 9 | && dnf clean all 10 | 11 | USER vscode 12 | 13 | RUN cargo install --force diesel_cli --no-default-features --features sqlite -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fido-device-onboard-rs", 3 | "build": { 4 | "dockerfile": "Dockerfile" 5 | }, 6 | "containerUser": "vscode", 7 | "updateRemoteUserUID": true, 8 | "containerEnv": { 9 | "HOME": "/home/vscode" 10 | }, 11 | "runArgs": [ 12 | "--userns=keep-id:uid=1000,gid=1000", 13 | "--cap-add=SYS_PTRACE", 14 | "--security-opt", 15 | "seccomp=unconfined" 16 | ], 17 | "settings": { 18 | "files.watcherExclude": { 19 | "**/target/**": true 20 | }, 21 | "rust-analyzer.checkOnSave.command": "clippy" 22 | }, 23 | "extensions": [ 24 | "mutantdino.resourcemonitor", 25 | "matklad.rust-analyzer", 26 | "tamasfe.even-better-toml", 27 | "serayuzgur.crates", 28 | "rust-lang.rust-analyzer" 29 | ], 30 | "remoteEnv": { 31 | "PATH": "${containerEnv:PATH}:/home/vscode/.cargo/bin", 32 | "SQLITE_MANUFACTURER_DATABASE_URL": "../ci-manufacturer-db.sqlite", 33 | "SQLITE_OWNER_DATABASE_URL": "../ci-owner-db.sqlite", 34 | "SQLITE_RENDEZVOUS_DATABASE_URL": "../ci-rendezvous-db.sqlite" 35 | }, 36 | "containerEnv": { 37 | "SQLITE_MANUFACTURER_DATABASE_URL": "../ci-manufacturer-db.sqlite", 38 | "SQLITE_OWNER_DATABASE_URL": "../ci-owner-db.sqlite", 39 | "SQLITE_RENDEZVOUS_DATABASE_URL": "../ci-rendezvous-db.sqlite" 40 | }, 41 | "hostRequirements": { 42 | "memory": "4gb" 43 | }, 44 | "remoteUser": "vscode", 45 | "updateContentCommand": [ 46 | "cargo", 47 | "build" 48 | ], 49 | "postCreateCommand": "cargo install --force diesel_cli --no-default-features --features sqlite && diesel migration run --migration-dir ./migrations/migrations_manufacturing_server_sqlite --database-url ./ci-manufacturer-db.sqlite && diesel migration run --migration-dir ./migrations/migrations_owner_onboarding_server_sqlite --database-url ./ci-owner-db.sqlite && diesel migration run --migration-dir ./migrations/migrations_rendezvous_server_sqlite --database-url ./ci-rendezvous-db.sqlite", 50 | "waitFor": "onCreateCommand" 51 | } 52 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /.github/commitlint.config.mjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | const validateBodyMaxLengthIgnoringDeps = async (parsedCommit) => { 3 | const { maxLineLength } = await import('@commitlint/ensure'); 4 | 5 | const { type, scope, body } = parsedCommit 6 | const isDepsCommit = 7 | type === 'chore' 8 | && body != null 9 | && body.includes('Updates the requirements on'); 10 | 11 | const bodyMaxLineLength = 1000; 12 | 13 | return [ 14 | isDepsCommit || !body || maxLineLength(body, bodyMaxLineLength), 15 | `body's lines must not be longer than ${bodyMaxLineLength}`, 16 | ] 17 | } 18 | 19 | export default { 20 | extends: ['@commitlint/config-conventional'], 21 | plugins: ['commitlint-plugin-function-rules'], 22 | rules: { 23 | 'body-max-line-length': [0], 24 | 'function-rules/body-max-line-length': [ 25 | 2, 26 | 'always', 27 | validateBodyMaxLengthIgnoringDeps, 28 | ], 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | commit-message: 8 | prefix: "chore: " 9 | reviewers: 10 | - "fdo-rs/fdo-team" 11 | - package-ecosystem: "github-actions" 12 | directory: "/" 13 | schedule: 14 | interval: "daily" 15 | commit-message: 16 | prefix: "chore: " 17 | reviewers: 18 | - "fdo-rs/fdo-team" 19 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | possible stability impact: 2 | - libfdo-data/fdo_data.h 3 | - libfdo-data/libfdo-data-go.doc 4 | -------------------------------------------------------------------------------- /.github/spellcheck-ignore: -------------------------------------------------------------------------------- 1 | crate 2 | mut 3 | stdio 4 | ser 5 | childs 6 | ot 7 | marshalling 8 | te 9 | -------------------------------------------------------------------------------- /.github/workflows/analysis.yml: -------------------------------------------------------------------------------- 1 | name: Code analysis 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '19 20 * * 6' 8 | 9 | jobs: 10 | analysis_devskim: 11 | name: DevSkim 12 | runs-on: ubuntu-latest 13 | permissions: 14 | actions: read 15 | contents: read 16 | security-events: write 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 22 | 23 | - name: Run DevSkim scanner 24 | uses: microsoft/DevSkim-Action@v1 25 | with: 26 | ignore-globs: '**/examples/**' 27 | 28 | - name: Upload DevSkim scan results to GitHub Security tab 29 | uses: github/codeql-action/upload-sarif@v3 30 | with: 31 | sarif_file: devskim-results.sarif 32 | -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | - "*.*.x" 6 | tags: 7 | - v* 8 | schedule: 9 | - cron: '0 0 * * *' 10 | 11 | name: Continuous Delivery 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | environment: continuous_delivery 17 | env: 18 | quay_org: fido-fdo 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - name: Perform container builds 24 | id: build 25 | uses: ./.github/actions/build_containers 26 | 27 | - name: Push admin-cli to quay.io 28 | uses: redhat-actions/push-to-registry@v2 29 | with: 30 | registry: "quay.io/${{ env.quay_org }}" 31 | image: admin-cli 32 | tags: ${{ steps.build.outputs.tags }} 33 | username: ${{ secrets.QUAY_USERNAME }} 34 | password: ${{ secrets.QUAY_ROBOT_TOKEN }} 35 | 36 | - name: Push manufacturing-server to quay.io 37 | uses: redhat-actions/push-to-registry@v2 38 | with: 39 | registry: "quay.io/${{ env.quay_org }}" 40 | image: manufacturing-server 41 | tags: ${{ steps.build.outputs.tags }} 42 | username: ${{ secrets.QUAY_USERNAME }} 43 | password: ${{ secrets.QUAY_ROBOT_TOKEN }} 44 | 45 | - name: Push rendezvous-server to quay.io 46 | uses: redhat-actions/push-to-registry@v2 47 | with: 48 | registry: "quay.io/${{ env.quay_org }}" 49 | image: rendezvous-server 50 | tags: ${{ steps.build.outputs.tags }} 51 | username: ${{ secrets.QUAY_USERNAME }} 52 | password: ${{ secrets.QUAY_ROBOT_TOKEN }} 53 | 54 | - name: Push serviceinfo-api-server to quay.io 55 | uses: redhat-actions/push-to-registry@v2 56 | with: 57 | registry: "quay.io/${{ env.quay_org }}" 58 | image: serviceinfo-api-server 59 | tags: ${{ steps.build.outputs.tags }} 60 | username: ${{ secrets.QUAY_USERNAME }} 61 | password: ${{ secrets.QUAY_ROBOT_TOKEN }} 62 | 63 | - name: Push owner-onboarding-server to quay.io 64 | uses: redhat-actions/push-to-registry@v2 65 | with: 66 | registry: "quay.io/${{ env.quay_org }}" 67 | image: owner-onboarding-server 68 | tags: ${{ steps.build.outputs.tags }} 69 | username: ${{ secrets.QUAY_USERNAME }} 70 | password: ${{ secrets.QUAY_ROBOT_TOKEN }} 71 | 72 | - name: Push aio to quay.io 73 | uses: redhat-actions/push-to-registry@v2 74 | with: 75 | registry: "quay.io/${{ env.quay_org }}" 76 | image: aio 77 | tags: ${{ steps.build.outputs.tags }} 78 | username: ${{ secrets.QUAY_USERNAME }} 79 | password: ${{ secrets.QUAY_ROBOT_TOKEN }} 80 | 81 | - name: Push client-linuxapp to quay.io 82 | uses: redhat-actions/push-to-registry@v2 83 | with: 84 | registry: "quay.io/${{ env.quay_org }}" 85 | image: client-linuxapp 86 | tags: ${{ steps.build.outputs.tags }} 87 | username: ${{ secrets.QUAY_USERNAME }} 88 | password: ${{ secrets.QUAY_ROBOT_TOKEN }} 89 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "Pull Request Labeler" 2 | on: 3 | - pull_request_target 4 | 5 | permissions: 6 | contents: read 7 | pull-requests: write 8 | 9 | jobs: 10 | triage: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/labeler@v5 14 | with: 15 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 16 | sync-labels: true 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # These are backup files generated by rustfmt 6 | **/*.rs.bk 7 | 8 | /testfiles 9 | 10 | # Jekyll output 11 | /docs/_site/ 12 | /docs/.jekyll-cache/ 13 | /docs/Gemfile.lock 14 | # RPM build directory 15 | /rpmbuild 16 | 17 | # Vendor directory 18 | /vendor 19 | 20 | # Source and vendor tar files 21 | fido-device-onboard-rs-*.tar.gz 22 | fido-device-onboard-rs-*-vendor-patched.tar.xz 23 | -------------------------------------------------------------------------------- /.gitleaks.toml: -------------------------------------------------------------------------------- 1 | [allowlist] 2 | description = "Allowlist" 3 | paths = ['''integration-tests\/vouchers\/([a-z0-9_]*)'''] 4 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | queue_rules: 2 | - name: duplicated fido-device-onboard-rs from Automatic merge on approval 3 | queue_conditions: 4 | - base=main 5 | - "#approved-reviews-by>=1" 6 | - "label!=possible stability impact" 7 | merge_conditions: 8 | - base=main 9 | - "#approved-reviews-by>=1" 10 | merge_method: merge 11 | - name: duplicated fido-device-onboard-rs from Automatic merge on approval (stability 12 | impact no impact) 13 | queue_conditions: 14 | - base=main 15 | - "#approved-reviews-by>=1" 16 | - "label=possible stability impact" 17 | - "label=stability impact assessed: no impact" 18 | merge_conditions: 19 | - base=main 20 | - "#approved-reviews-by>=1" 21 | merge_method: merge 22 | - name: duplicated fido-device-onboard-rs from Automatic merge on approval (stability 23 | impact) 24 | queue_conditions: 25 | - base=main 26 | - "#approved-reviews-by>=2" 27 | - "label=possible stability impact" 28 | merge_conditions: 29 | - base=main 30 | - "#approved-reviews-by>=1" 31 | merge_method: merge 32 | 33 | pull_request_rules: 34 | - name: Automatic merge on approval + Automatic merge on approval (stability impact 35 | no impact) + Automatic merge on approval (stability impact) 36 | conditions: [] 37 | actions: 38 | queue: 39 | -------------------------------------------------------------------------------- /.packit.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://packit.dev/docs/configuration/ 3 | 4 | specfile_path: fido-device-onboard.spec 5 | 6 | files_to_sync: 7 | - src: 8 | - ".packit.yaml" 9 | - "fido-device-onboard.spec" 10 | - "fido-device-onboard-rs-*-vendor-patched.tar.xz" 11 | - "patches/0001-use-released-aws-nitro-enclaves-cose-version.patch" 12 | dest: . 13 | 14 | upstream_package_name: fido-device-onboard 15 | downstream_package_name: fido-device-onboard 16 | 17 | upstream_tag_template: v{version} 18 | copy_upstream_release_description: true 19 | 20 | srpm_build_deps: 21 | - cargo 22 | - openssl-devel 23 | 24 | packages: 25 | fido-device-onboard-fedora: 26 | downstream_package_name: fido-device-onboard 27 | upstream_package_name: fido-device-onboard 28 | fido-device-onboard-centos: 29 | downstream_package_name: fido-device-onboard 30 | upstream_package_name: fido-device-onboard 31 | pkg_tool: centpkg 32 | 33 | actions: 34 | # for the propose-downstream CLI command 35 | pre-sync: 36 | - bash -c "make vendor-tarball VERSION=${PACKIT_PROJECT_VERSION}" 37 | # for the srpm and copr builds 38 | create-archive: 39 | - bash -c "make packit-create-archive VERSION=${PACKIT_PROJECT_VERSION}" 40 | 41 | jobs: 42 | 43 | # Fedora jobs 44 | 45 | - &fdo_copr_build_fedora 46 | job: copr_build 47 | packages: [fido-device-onboard-fedora] 48 | trigger: pull_request 49 | targets: ["fedora-latest-stable", "fedora-latest", "fedora-rawhide"] 50 | 51 | - <<: *fdo_copr_build_fedora 52 | trigger: commit 53 | branch: main 54 | owner: "@fedora-iot" 55 | project: fedora-iot 56 | 57 | - job: tests 58 | trigger: pull_request 59 | identifier: onboarding-fedora 60 | fmf_path: test/fmf 61 | tmt_plan: plans/onboarding 62 | packages: [fido-device-onboard-fedora] 63 | targets: ["fedora-latest-stable", "fedora-latest", "fedora-rawhide"] 64 | 65 | - job: sync_from_downstream 66 | trigger: commit 67 | 68 | - job: propose_downstream 69 | trigger: release 70 | packages: [fido-device-onboard-fedora] 71 | dist_git_branches: ["fedora-development", "fedora-latest-stable"] 72 | 73 | - job: koji_build 74 | trigger: commit 75 | allowed_pr_authors: [all_committers] 76 | dist_git_branches: ["fedora-development", "fedora-latest-stable"] 77 | 78 | - job: bodhi_update 79 | trigger: commit 80 | allowed_builders: [all_committers] 81 | dist_git_branches: ["fedora-development", "fedora-latest-stable"] 82 | 83 | # CentOS jobs 84 | 85 | - &fdo_copr_build_centos 86 | job: copr_build 87 | packages: [fido-device-onboard-centos] 88 | trigger: pull_request 89 | targets: ["centos-stream-9", "centos-stream-10"] 90 | 91 | - <<: *fdo_copr_build_centos 92 | trigger: commit 93 | branch: main 94 | owner: "@fedora-iot" 95 | project: fedora-iot 96 | 97 | - job: tests 98 | trigger: pull_request 99 | identifier: onboarding-centos 100 | fmf_path: test/fmf 101 | tmt_plan: plans/onboarding 102 | packages: [fido-device-onboard-centos] 103 | targets: ["centos-stream-9", "centos-stream-10"] 104 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "libfdo-data", 4 | "data-formats", 5 | "http-wrapper", 6 | "store", 7 | "util", 8 | "db", 9 | 10 | "client-linuxapp", 11 | "owner-onboarding-server", 12 | "owner-tool", 13 | "rendezvous-server", 14 | "manufacturing-server", 15 | "manufacturing-client", 16 | "serviceinfo-api-server", 17 | "admin-tool", 18 | 19 | "integration-tests", 20 | ] 21 | 22 | default-members = [ 23 | "libfdo-data", 24 | "data-formats", 25 | "http-wrapper", 26 | "store", 27 | "util", 28 | "db", 29 | 30 | "client-linuxapp", 31 | "owner-onboarding-server", 32 | "owner-tool", 33 | "rendezvous-server", 34 | "manufacturing-server", 35 | "manufacturing-client", 36 | "serviceinfo-api-server", 37 | "admin-tool", 38 | ] 39 | 40 | resolver = "2" 41 | 42 | [patch.crates-io] 43 | aws-nitro-enclaves-cose = { git = "https://github.com/awslabs/aws-nitro-enclaves-cose/", rev = "6064f826d551a9db0bd42e9cf928feaf272e8d17" } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Red Hat, Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fido-device-onboard-rs 2 | An implementation of the FIDO Device Onboard Specification written in rust. 3 | 4 | The current implementation targets specification version: [1.1 20211214](https://fidoalliance.org/specs/FDO/FIDO-Device-Onboard-RD-v1.1-20211214/FIDO-device-onboard-spec-v1.1-rd-20211214.html). 5 | 6 | ## Components 7 | The fido-fdo-rs implements all core components of the FIDO Device Onboard Specification including: 8 | - [Client](https://github.com/fedora-iot/fido-device-onboard-rs/tree/main/client-linuxapp) 9 | - [Rendezvous Server](https://github.com/fedora-iot/fido-device-onboard-rs/tree/main/rendezvous-server) 10 | - [Onboarding Server](https://github.com/fedora-iot/fido-device-onboard-rs/tree/main/owner-onboarding-server) 11 | - Manufacturing Tool both [client](https://github.com/fedora-iot/fido-device-onboard-rs/tree/main/manufacturing-client) and [server](https://github.com/fedora-iot/fido-device-onboard-rs/tree/main/manufacturing-server) 12 | 13 | ## Protocols 14 | - [Device Initialize Protocol (DI)](https://fidoalliance.org/specs/FDO/FIDO-Device-Onboard-RD-v1.1-20211214/FIDO-device-onboard-spec-v1.1-rd-20211214.html#device-initialize-protocol-di) 15 | - [Transfer Ownership Protocol 0 (TO0)](https://fidoalliance.org/specs/FDO/FIDO-Device-Onboard-RD-v1.1-20211214/FIDO-device-onboard-spec-v1.1-rd-20211214.html#transfer-ownership-protocol-0-to0) 16 | - [Transfer Ownership Protocol 1 (TO1)](https://fidoalliance.org/specs/FDO/FIDO-Device-Onboard-RD-v1.1-20211214/FIDO-device-onboard-spec-v1.1-rd-20211214.html#transfer-ownership-protocol-1-to1) 17 | - [Transfer Ownership Protocol 2 (TO2)](https://fidoalliance.org/specs/FDO/FIDO-Device-Onboard-RD-v1.1-20211214/FIDO-device-onboard-spec-v1.1-rd-20211214.html#transfer-ownership-protocol-2-to2) 18 | 19 | ## Crates and parts 20 | - `fdo-client-linuxapp`: Performs TO1 and TO2 client side protocols. 21 | - `fdo-data-formats`: [DI, TO0, TO1, TO2]: Implements the different low-level messaging formats used. 22 | - `fdo-http-wrapper`: Helpers for HTTP operations in both FDO server and client. 23 | - `fdo-integration-tests`: This crate contains the integration testing. 24 | - `fdo-libfdo-data`: C wrapper around `fdo-data-formats`, allowing code in other languages to parse Ownership Vouchers, and possibly other data formats in the future. 25 | - `fdo-manufacturing-client`: Client side implementation of Device Initialize and Device Initialize over 26 | Untrusted Networks (DIUN) protocols. 27 | - `fdo-manufacturing-server`: Server side implementation of Device Initialize protocol. It supports as well Untrusted Networks (DIUN) protocols, that can be used for local prototypes. 28 | - `fdo-owner-onboarding-server`: Onboarding server, server side of TO2 protocol. 29 | - `fdo-owner-tool`: Tool for initializing devices, dump ownership vouchers, dump device credentials, extend ownership vouchers and report the device to the rendezvous service. 30 | - `fdo-rendezvous-server`: Rendezvous server implementation. 31 | - `fdo-store`: Implementation of different backend datastores for services. 32 | - `fdo-util`: Utilities/helpers for server (and, in the future client) crates. 33 | - `fdo-iot-stream-message`: Implements the stream message creation/parsing of StreamMsg. Currently not implemented. 34 | - `fdo-serviceinfo-api-server`: Service Info API Server implementation. The specification is written in [serviceinfo_api.md](./docs/specs/serviceinfo_api.md). 35 | 36 | ## RPMs and containers 37 | 38 | This project currently releases RPMs and containers tracking the `main` branch. RPMs are available in [COPR](https://copr.fedorainfracloud.org/coprs/g/fedora-iot/fedora-iot/). Containers are available on [Quay.io](https://quay.io/organization/fido-fdo). 39 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | Releasing a new version 2 | ======================= 3 | 4 | We will use the `v0.5.3` release (#738) as an example of how to release a new 5 | FDO version: 6 | 7 | * Fork the repo and create a new branch for the new release: 8 | 9 | ```bash 10 | gh repo fork fdo-rs/fido-device-onboard-rs --clone --remote 11 | git pull upstream main 12 | git checkout -b prepare-v0.5.3 13 | ``` 14 | 15 | * Update the `fido-device-onboard.spec` file and set the new version: `Version: 0.5.3` 16 | * Update the `version` within the `[[package]]` section in all the `Cargo.toml` files. 17 | * Update the `[[dependencies]]` in all the `Cargo.toml` files to use the latest 18 | FDO versions. 19 | * Update the `libfdo-data/fdo_data.h` file to reflect the correct version: 20 | 21 | ```c 22 | #define FDO_DATA_MAJOR 0 23 | #define FDO_DATA_MINOR 5 24 | #define FDO_DATA_PATCH 3 25 | ``` 26 | 27 | * Update `Cargo.lock` file: 28 | 29 | ```bash 30 | cargo update --offline --workspace 31 | ``` 32 | 33 | * Commit all the changes and create a PR (see #738 with all the changes described 34 | above): 35 | 36 | ```bash 37 | git add fido-device-onboard.spec Cargo.toml Cargo.lock */Cargo.toml libfdo-data/fdo_data.h 38 | git commit -s -m "chore: bump for 0.5.3 release" -m "Prepare for the 0.5.3 release." 39 | gh pr create 40 | ``` 41 | 42 | * Once all the tests pass and the PR is merged, tag and sign the release: 43 | 44 | ```bash 45 | git tag -a -s v0.5.3 46 | git push upstream v0.5.3 47 | ``` 48 | 49 | * Using the webui, open the [Releases](https://github.com/fdo-rs/fido-device-onboard-rs/releases) 50 | page and click the "Draft a new release" button in the middle of the page. From 51 | there you can choose the `v0.5.3` tag you created in the previous step. 52 | * Use the version as the "Release title" and keep the format i.e. "v0.5.3". 53 | * In the description add in any release notes. When satisfied, click the 54 | "Save draft" or "Publish release" button at the bottom of the page. 55 | -------------------------------------------------------------------------------- /admin-tool/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fdo-admin-tool" 3 | version = "0.5.5" 4 | authors = ["Antonio Murdaca "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1" 11 | config = "0.13.4" 12 | openssl = "0.10.72" 13 | log = "0.4" 14 | time = "0.3" 15 | clap = { version = "4.4", features = ["derive"] } 16 | futures = "0.3" 17 | reqwest = "0.12" 18 | serde = "1" 19 | serde_yaml = "0.9" 20 | pretty_env_logger = "0.5" 21 | nix = "0.26" 22 | tokio = { version = "1", features = ["full"] } 23 | 24 | fdo-data-formats = { path = "../data-formats", version = "0.5.5" } 25 | fdo-http-wrapper = { path = "../http-wrapper", version = "0.5.5", features = ["server", "client"] } 26 | fdo-store = { path = "../store", version = "0.5.5", features = ["directory"] } 27 | fdo-util = { path = "../util", version = "0.5.5" } 28 | clap_builder = "4.4" 29 | 30 | [dev-dependencies] 31 | rand = "0.8" 32 | -------------------------------------------------------------------------------- /admin-tool/src/aio/device.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | 3 | use anyhow::{bail, Context, Result}; 4 | use clap::{Args, Subcommand}; 5 | 6 | use super::{configure::Configuration, ChildBinary}; 7 | 8 | #[derive(Debug, Args)] 9 | pub(super) struct DeviceArgs { 10 | #[clap(subcommand)] 11 | command: DeviceSubcommand, 12 | 13 | #[clap(long)] 14 | device_credential_location: PathBuf, 15 | } 16 | 17 | #[derive(Debug, Subcommand)] 18 | enum DeviceSubcommand { 19 | Manufacture(DeviceManufactureArgs), 20 | Analyze, 21 | Run(DeviceRunArgs), 22 | } 23 | 24 | #[derive(Debug, Args)] 25 | struct DeviceManufactureArgs { 26 | #[clap(long)] 27 | device_info: String, 28 | } 29 | 30 | #[derive(Debug, Args)] 31 | struct DeviceRunArgs { 32 | #[clap(long)] 33 | allow_noninteroperable_kdf: bool, 34 | } 35 | 36 | async fn run_device( 37 | _aio_dir: PathBuf, 38 | binary_path: PathBuf, 39 | _configuration: &Configuration, 40 | args: &DeviceArgs, 41 | run_args: &DeviceRunArgs, 42 | ) -> Result<()> { 43 | let marker_location = format!( 44 | "{}.marker", 45 | args.device_credential_location.to_string_lossy() 46 | ); 47 | 48 | println!("========== STARTING CLIENT =========="); 49 | let mut command = 50 | tokio::process::Command::new(binary_path.join(ChildBinary::ClientLinuxapp.binary_name())); 51 | command 52 | .env("LOG_LEVEL", "trace") 53 | .env("DEVICE_CREDENTIAL", &args.device_credential_location) 54 | .env( 55 | "DEVICE_ONBOARDING_EXECUTED_MARKER_FILE_PATH", 56 | &marker_location, 57 | ) 58 | .kill_on_drop(true); 59 | 60 | if run_args.allow_noninteroperable_kdf { 61 | command.env("ALLOW_NONINTEROPERABLE_KDF", "true"); 62 | } 63 | 64 | let status = command 65 | .status() 66 | .await 67 | .context("Error starting the client")?; 68 | println!("========== CLIENT ENDED WITH STATUS: {status:?} ==========",); 69 | 70 | if status.success() { 71 | log::info!("Device onboarding completed"); 72 | Ok(()) 73 | } else { 74 | bail!("Client failed with status: {:?}", status); 75 | } 76 | } 77 | 78 | async fn manufacture_device( 79 | aio_dir: PathBuf, 80 | binary_path: PathBuf, 81 | configuration: &Configuration, 82 | args: &DeviceArgs, 83 | mfg_args: &DeviceManufactureArgs, 84 | ) -> Result<()> { 85 | println!("========== STARTING MANUFACTURING CLIENT =========="); 86 | let status = tokio::process::Command::new( 87 | binary_path.join(ChildBinary::ManufacturingClient.binary_name()), 88 | ) 89 | .env("LOG_LEVEL", "trace") 90 | .env( 91 | "DIUN_PUB_KEY_ROOTCERTS", 92 | aio_dir.join("keys").join("diun_cert.pem"), 93 | ) 94 | .env( 95 | "MANUFACTURING_SERVER_URL", 96 | format!( 97 | "http://localhost:{}", //DevSkim: ignore DS137138 98 | configuration.listen_port_manufacturing_server 99 | ), 100 | ) 101 | .env("DI_MFG_STRING_TYPE", "serialnumber") 102 | .env("MANUFACTURING_INFO", &mfg_args.device_info) 103 | .env( 104 | "DEVICE_CREDENTIAL_FILENAME", 105 | &args.device_credential_location, 106 | ) 107 | .kill_on_drop(true) 108 | .status() 109 | .await 110 | .context("Error running manufacturing client")?; 111 | println!("========== MANUFACTURING CLIENT ENDED WITH STATUS: {status:?} =========="); 112 | 113 | if status.success() { 114 | log::info!("Device manufacturing completed"); 115 | print_device_credential(binary_path, &args.device_credential_location).await 116 | } else { 117 | bail!("Manufacturing client failed with status: {:?}", status); 118 | } 119 | } 120 | 121 | async fn print_device_credential( 122 | binary_path: PathBuf, 123 | device_credential_path: &Path, 124 | ) -> Result<()> { 125 | let status = 126 | tokio::process::Command::new(binary_path.join(ChildBinary::OwnerTool.binary_name())) 127 | .arg("dump-device-credential") 128 | .arg(device_credential_path) 129 | .status() 130 | .await 131 | .context("Error running owner-tool to dump device credential")?; 132 | 133 | if status.success() { 134 | Ok(()) 135 | } else { 136 | bail!("Owner-tool failed with status: {:?}", status) 137 | } 138 | } 139 | 140 | pub(super) async fn run_device_subcommand( 141 | aio_dir: PathBuf, 142 | binary_path: PathBuf, 143 | configuration: &Configuration, 144 | args: &DeviceArgs, 145 | ) -> Result<()> { 146 | match &args.command { 147 | DeviceSubcommand::Manufacture(mfg_args) => { 148 | manufacture_device(aio_dir, binary_path, configuration, args, mfg_args) 149 | .await 150 | .context("Error manufacturing device") 151 | } 152 | DeviceSubcommand::Analyze => { 153 | print_device_credential(binary_path, &args.device_credential_location) 154 | .await 155 | .context("Error analyzing device credential") 156 | } 157 | DeviceSubcommand::Run(run_args) => { 158 | run_device(aio_dir, binary_path, configuration, args, run_args) 159 | .await 160 | .context("Error running device client") 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /bundled-provides.jq: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S jq --sort-keys --raw-output --from-file 2 | 3 | # To be run from a cargo workspace as: 4 | # cargo metadata --locked --format-version 1 | CRATE_NAME="zincati" bundled-provides.jq 5 | 6 | .packages[] | 7 | select(.name != env.CRATE_NAME) | 8 | "Provides: " + 9 | "bundled(crate(" + .name + "))" + 10 | " = " + 11 | ( .version | gsub("-"; "_") ) -------------------------------------------------------------------------------- /client-linuxapp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fdo-client-linuxapp" 3 | version = "0.5.5" 4 | authors = ["Patrick Uiterwijk "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1" 11 | log = "0.4" 12 | tokio = { version = "1", features = ["full"] } 13 | sys-info = "0.9" 14 | serde_bytes = "0.11" 15 | rand = "0.8.4" 16 | nix = "0.26" 17 | uuid = "1.3" 18 | thiserror = "1" 19 | libcryptsetup-rs = { version = ">= 0.11.2", features = ["mutex"] } 20 | secrecy = "0.8" 21 | devicemapper = "0.34" 22 | openssl = "0.10.72" 23 | 24 | fdo-data-formats = { path = "../data-formats", version = "0.5.5" } 25 | fdo-http-wrapper = { path = "../http-wrapper", version = "0.5.5", features = ["client"] } 26 | fdo-util = { path = "../util", version = "0.5.5" } 27 | -------------------------------------------------------------------------------- /contrib/containers/README.md: -------------------------------------------------------------------------------- 1 | # Using the FIDO-Device-Onboard-RS containers 2 | ## Generating the Certificates and keys 3 | 4 | Generate all the certificates and keys to be used with the various FDO 5 | containers. 6 | 7 | ``` bash 8 | mkdir keys 9 | for i in "diun" "manufacturer" "device-ca" "owner"; do fdo-admin-tool generate-key-and-cert $i; done 10 | ``` 11 | # Copy and edit the configuration files 12 | 13 | Copy the configuration file from the examples provided and edit as needed. 14 | 15 | - [manufacturing-server.yml](https://github.com/fedora-iot/fido-device-onboard-rs/blob/main/examples/config/manufacturing-server.yml) 16 | - [owner-onboarding-server.yml](https://github.com/fedora-iot/fido-device-onboard-rs/blob/main/examples/config/owner-onboarding-server.yml) 17 | - [rendezvous-server.yml](https://github.com/fedora-iot/fido-device-onboard-rs/blob/main/examples/config/rendezvous-server.yml) 18 | - [serviceinfo-api-server.yml](https://github.com/fedora-iot/fido-device-onboard-rs/blob/main/examples/config/serviceinfo-api-server.yml) 19 | 20 | ## Running the FDO containers 21 | 22 | ### manufacturing-server 23 | 24 | ``` bash 25 | podman pull quay.io/fido-fdo/manufacturing-server 26 | 27 | podman run -d \ 28 | --name manufacturing-server \ 29 | -p 8080:8080 \ 30 | -v /local/path/to/keys/:/etc/fdo/keys:Z \ 31 | -v /local/path/to/config:/etc/fdo/manufacturing-server.conf.d/:Z \ 32 | quay.io/fido-fdo/manufacturing-server 33 | ``` 34 | 35 | ### owner-onboarding-server 36 | 37 | ``` bash 38 | podman pull quay.io/fido-fdo/owner-onboarding-server 39 | 40 | podman run -d \ 41 | --name owner-onboarding-server \ 42 | -p 8081:8081 \ 43 | -v /local/path/to/keys/:/etc/fdo/keys:Z \ 44 | -v /local/path/to/config:/etc/fdo/owner-onboarding-server.conf.d/:Z \ 45 | quay.io/fido-fdo/owner-onboarding-server 46 | ``` 47 | 48 | ### rendezvous-server 49 | 50 | ``` bash 51 | podman pull quay.io/fido-fdo/rendezvous-server 52 | 53 | podman run -d \ 54 | --name rendezvous-server \ 55 | -p 8082:8082 \ 56 | -v /local/path/to/keys/:/etc/fdo/keys:Z \ 57 | -v /local/path/to/config:/etc/fdo/rendezvous-server.conf.d/:Z \ 58 | quay.io/fido-fdo/rendezvous-server 59 | ``` 60 | 61 | ### serviceinfo-api-server 62 | 63 | ``` bash 64 | podman pull quay.io/fido-fdo/serviceinfo-api-server 65 | 66 | podman run -d \ 67 | --name serviceinfo-api-server \ 68 | -p 8083:8083 \ 69 | -v /local/path/to/config:/etc/fdo/serviceinfo-api-server.conf.d/:Z \ 70 | -v /local/path/to/device_specific_serviceinfo:/etc/fdo/device_specific_serviceinfo/:Z \ 71 | quay.io/fido-fdo/serviceinfo-api-server 72 | ``` 73 | 74 | ### client-linuxapp 75 | 76 | ``` bash 77 | podman pull quay.io/fido-fdo/client-linuxapp 78 | 79 | podman run -d \ 80 | --name client-linuxapp \ 81 | -v /local/path/to/device_credential/directory:/etc/fdo/:Z \ 82 | -e DEVICE_CREDENTIAL=/etc/fdo/device-credentials \ 83 | quay.io/fido-fdo/client-linuxapp 84 | ``` -------------------------------------------------------------------------------- /contrib/containers/admin-cli: -------------------------------------------------------------------------------- 1 | FROM quay.io/centos/centos:stream9 2 | ARG BUILDID 3 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-admin-tool /usr/local/bin 4 | RUN yum install -y sqlite libpq 5 | ENTRYPOINT ["fdo-admin-tool"] 6 | -------------------------------------------------------------------------------- /contrib/containers/aio: -------------------------------------------------------------------------------- 1 | FROM quay.io/centos/centos:stream9 2 | ARG BUILDID 3 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-admin-tool /usr/bin 4 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-manufacturing-server /usr/bin 5 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-owner-onboarding-server /usr/bin 6 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-rendezvous-server /usr/bin 7 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-serviceinfo-api-server /usr/bin 8 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-client-linuxapp /usr/bin 9 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-owner-tool /usr/bin 10 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-manufacturing-client /usr/bin 11 | RUN yum install -y sqlite libpq cryptsetup-libs clevis clevis-luks 12 | ENTRYPOINT ["fdo-admin-tool"] 13 | -------------------------------------------------------------------------------- /contrib/containers/build: -------------------------------------------------------------------------------- 1 | FROM quay.io/centos/centos:stream9 2 | RUN yum update -y 3 | RUN yum install -y --enablerepo=crb cargo gcc golang openssl-devel tpm2-tss-devel cryptsetup-devel clang-devel sqlite sqlite-devel libpq libpq-devel 4 | WORKDIR /usr/src 5 | COPY . . 6 | RUN cargo build --release --features openssl-kdf/deny_custom 7 | -------------------------------------------------------------------------------- /contrib/containers/client-linuxapp: -------------------------------------------------------------------------------- 1 | FROM quay.io/centos/centos:stream9 2 | ARG BUILDID 3 | RUN yum install -y cryptsetup-libs clevis clevis-luks 4 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-client-linuxapp /usr/local/bin 5 | ENV LOG_LEVEL=trace 6 | ENTRYPOINT ["fdo-client-linuxapp"] 7 | -------------------------------------------------------------------------------- /contrib/containers/manufacturing-server: -------------------------------------------------------------------------------- 1 | FROM quay.io/centos/centos:stream9 2 | ARG BUILDID 3 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-manufacturing-server /usr/local/bin 4 | RUN mkdir -p /etc/fdo/sessions 5 | RUN mkdir -p /etc/fdo/keys 6 | RUN mkdir -p /etc/fdo/manufacturing-server.conf.d 7 | RUN yum install -y sqlite libpq 8 | ENV LOG_LEVEL=trace 9 | ENTRYPOINT ["fdo-manufacturing-server"] 10 | -------------------------------------------------------------------------------- /contrib/containers/owner-onboarding-server: -------------------------------------------------------------------------------- 1 | FROM quay.io/centos/centos:stream9 2 | ARG BUILDID 3 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-owner-onboarding-server /usr/local/bin 4 | RUN mkdir -p /etc/fdo/sessions 5 | RUN mkdir -p /etc/fdo/keys 6 | RUN mkdir -p /etc/fdo/owner-onboarding-server.conf.d 7 | RUN yum install -y sqlite libpq 8 | ENV LOG_LEVEL=trace 9 | ENTRYPOINT ["fdo-owner-onboarding-server"] 10 | -------------------------------------------------------------------------------- /contrib/containers/rendezvous-server: -------------------------------------------------------------------------------- 1 | FROM quay.io/centos/centos:stream9 2 | ARG BUILDID 3 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-rendezvous-server /usr/local/bin 4 | RUN mkdir -p /etc/fdo/sessions 5 | RUN mkdir -p /etc/fdo/keys 6 | RUN mkdir -p /etc/fdo/rendezvous-server.conf.d 7 | RUN yum install -y sqlite libpq 8 | ENV LOG_LEVEL=trace 9 | ENTRYPOINT ["fdo-rendezvous-server"] 10 | -------------------------------------------------------------------------------- /contrib/containers/serviceinfo-api-server: -------------------------------------------------------------------------------- 1 | FROM quay.io/centos/centos:stream9 2 | ARG BUILDID 3 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-serviceinfo-api-server /usr/local/bin 4 | RUN mkdir -p /etc/fdo/sessions 5 | RUN mkdir -p /etc/fdo/device_specific_serviceinfo 6 | RUN mkdir -p /etc/fdo/serviceinfo-api-server.conf.d 7 | RUN yum install -y sqlite libpq 8 | ENV LOG_LEVEL=trace 9 | ENTRYPOINT ["fdo-serviceinfo-api-server"] 10 | -------------------------------------------------------------------------------- /data-formats/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fdo-data-formats" 3 | version = "0.5.5" 4 | authors = ["Patrick Uiterwijk "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | ciborium = "0.2.0" 11 | hex = "0.4" 12 | openssl = "0.10.72" 13 | log = "0.4" 14 | serde = "1" 15 | serde_bytes = "0.11" 16 | serde_cbor = "0.11" 17 | serde_repr = "0.1.19" 18 | serde_tuple = "0.5" 19 | thiserror = "1" 20 | aws-nitro-enclaves-cose = "0.5.2" 21 | uuid = "1.3" 22 | num-traits = "0.2" 23 | num-derive = "0.4" 24 | paste = "1.0" 25 | pem = "3.0" 26 | tss-esapi = { version = "7.6", features = ["generate-bindings"] } 27 | byteorder = "1" 28 | 29 | http = "0.2" 30 | 31 | openssl-kdf = { version = "0.4.2", features = ["allow_custom"] } 32 | 33 | [features] 34 | # Whether to use a non-interoperable KDF. 35 | use_noninteroperable_kdf = [] 36 | 37 | [build-dependencies] 38 | openssl-kdf = { version = "0.4.2", features = ["allow_custom"] } 39 | 40 | [dev-dependencies] 41 | maplit = "1.0" 42 | -------------------------------------------------------------------------------- /data-formats/build.rs: -------------------------------------------------------------------------------- 1 | use openssl_kdf::{supports_args, KdfArgument}; 2 | 3 | #[allow(clippy::panic)] 4 | fn main() { 5 | if std::env::var("CARGO_FEATURE_USE_NONINTEROPERABLE_KDF").is_err() { 6 | let test_args = &[ 7 | &KdfArgument::Salt(&[]), 8 | &KdfArgument::KbInfo(&[]), 9 | &KdfArgument::Key(&[]), 10 | &KdfArgument::UseL(false), 11 | &KdfArgument::R(8), 12 | ]; 13 | 14 | if !supports_args(test_args) { 15 | panic!( 16 | "\n\ 17 | Current KDF implementation does not support the interoperable parameters.\n\ 18 | If you still want to build, you can enable the non-interoperable KDF, but be aware that \ 19 | that implementation does not interoperate with FDO-spec-compliant implementations.\n\ 20 | " 21 | ); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /data-formats/src/constants/serviceinfo_names.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Display, str::FromStr}; 2 | 3 | impl<'de> serde::Deserialize<'de> for ServiceInfoModule { 4 | fn deserialize(deserializer: D) -> Result 5 | where 6 | D: serde::de::Deserializer<'de>, 7 | { 8 | struct SIMVisitor; 9 | impl serde::de::Visitor<'_> for SIMVisitor { 10 | type Value = ServiceInfoModule; 11 | 12 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 13 | formatter.write_str("a module string") 14 | } 15 | 16 | fn visit_str(self, value: &str) -> Result 17 | where 18 | E: serde::de::Error, 19 | { 20 | ServiceInfoModule::from_str(value).map_err(|e| E::custom(format!("{e}"))) 21 | } 22 | } 23 | deserializer.deserialize_string(SIMVisitor) 24 | } 25 | } 26 | 27 | impl serde::Serialize for ServiceInfoModule { 28 | fn serialize(&self, serializer: S) -> Result 29 | where 30 | S: serde::ser::Serializer, 31 | { 32 | serializer.serialize_str(&self.to_string()) 33 | } 34 | } 35 | 36 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] 37 | #[non_exhaustive] 38 | pub enum ServiceInfoModule { 39 | Standard(StandardServiceInfoModule), 40 | FedoraIot(FedoraIotServiceInfoModule), 41 | RedHatCom(RedHatComServiceInfoModule), 42 | Unsupported(String), 43 | } 44 | 45 | impl FromStr for ServiceInfoModule { 46 | type Err = crate::Error; 47 | 48 | fn from_str(s: &str) -> Result { 49 | Ok(match s { 50 | "org.fedoraiot.binaryfile" => FedoraIotServiceInfoModule::BinaryFile.into(), 51 | "org.fedoraiot.command" => FedoraIotServiceInfoModule::Command.into(), 52 | "org.fedoraiot.sshkey" => FedoraIotServiceInfoModule::SSHKey.into(), 53 | "org.fedoraiot.diskencryption-clevis" => { 54 | FedoraIotServiceInfoModule::DiskEncryptionClevis.into() 55 | } 56 | "org.fedoraiot.reboot" => FedoraIotServiceInfoModule::Reboot.into(), 57 | 58 | "com.redhat.subscriptionmanager" => { 59 | RedHatComServiceInfoModule::SubscriptionManager.into() 60 | } 61 | 62 | "devmod" => StandardServiceInfoModule::DevMod.into(), 63 | 64 | other => ServiceInfoModule::Unsupported(other.to_string()), 65 | }) 66 | } 67 | } 68 | 69 | impl From for ServiceInfoModule { 70 | fn from(module: StandardServiceInfoModule) -> Self { 71 | ServiceInfoModule::Standard(module) 72 | } 73 | } 74 | 75 | impl From for ServiceInfoModule { 76 | fn from(module: FedoraIotServiceInfoModule) -> Self { 77 | ServiceInfoModule::FedoraIot(module) 78 | } 79 | } 80 | 81 | impl From for ServiceInfoModule { 82 | fn from(module: RedHatComServiceInfoModule) -> Self { 83 | ServiceInfoModule::RedHatCom(module) 84 | } 85 | } 86 | 87 | impl Display for ServiceInfoModule { 88 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 89 | match self { 90 | ServiceInfoModule::Standard(module) => module.fmt(f), 91 | ServiceInfoModule::FedoraIot(module) => { 92 | write!(f, "org.fedoraiot.")?; 93 | Display::fmt(module, f) 94 | } 95 | ServiceInfoModule::RedHatCom(module) => { 96 | write!(f, "com.redhat.")?; 97 | Display::fmt(module, f) 98 | } 99 | ServiceInfoModule::Unsupported(other) => write!(f, "{other}"), 100 | } 101 | } 102 | } 103 | 104 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] 105 | #[non_exhaustive] 106 | pub enum StandardServiceInfoModule { 107 | DevMod, 108 | } 109 | 110 | impl Display for StandardServiceInfoModule { 111 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 112 | write!( 113 | f, 114 | "{}", 115 | match self { 116 | StandardServiceInfoModule::DevMod => "devmod", 117 | } 118 | ) 119 | } 120 | } 121 | 122 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] 123 | #[non_exhaustive] 124 | pub enum FedoraIotServiceInfoModule { 125 | Command, 126 | SSHKey, 127 | BinaryFile, 128 | DiskEncryptionClevis, 129 | Reboot, 130 | } 131 | 132 | impl Display for FedoraIotServiceInfoModule { 133 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 134 | write!( 135 | f, 136 | "{}", 137 | match self { 138 | FedoraIotServiceInfoModule::Command => "command", 139 | FedoraIotServiceInfoModule::SSHKey => "sshkey", 140 | FedoraIotServiceInfoModule::BinaryFile => "binaryfile", 141 | FedoraIotServiceInfoModule::DiskEncryptionClevis => "diskencryption-clevis", 142 | FedoraIotServiceInfoModule::Reboot => "reboot", 143 | } 144 | ) 145 | } 146 | } 147 | 148 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] 149 | #[non_exhaustive] 150 | pub enum RedHatComServiceInfoModule { 151 | SubscriptionManager, 152 | } 153 | 154 | impl Display for RedHatComServiceInfoModule { 155 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 156 | write!( 157 | f, 158 | "{}", 159 | match self { 160 | RedHatComServiceInfoModule::SubscriptionManager => "subscriptionmanager", 161 | } 162 | ) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /data-formats/src/devicecredential/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | errors::Error, 3 | types::{Guid, HMac, Hash, RendezvousInfo}, 4 | ProtocolVersion, 5 | }; 6 | 7 | pub trait DeviceCredential: std::fmt::Debug { 8 | fn is_active(&self) -> bool; 9 | fn protocol_version(&self) -> ProtocolVersion; 10 | fn verify_hmac(&self, data: &[u8], hmac: &HMac) -> Result<(), Error>; 11 | fn device_info(&self) -> &str; 12 | fn device_guid(&self) -> &Guid; 13 | fn rendezvous_info(&self) -> &RendezvousInfo; 14 | fn manufacturer_pubkey_hash(&self) -> &Hash; 15 | 16 | fn get_signer( 17 | &self, 18 | ) -> Result, Error>; 19 | } 20 | 21 | pub mod file; 22 | pub use crate::devicecredential::file::FileDeviceCredential; 23 | -------------------------------------------------------------------------------- /data-formats/src/errors.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | use crate::ProtocolVersion; 4 | 5 | pub type Result = std::result::Result; 6 | 7 | #[derive(Error, Debug)] 8 | pub enum ChainError { 9 | #[error("Chain is empty")] 10 | Empty, 11 | #[error("Invalid signed certificate at position {0}")] 12 | InvalidSignedCert(usize), 13 | #[error("No trusted root encountered")] 14 | NoTrustedRoot, 15 | #[error("Non-issuer certificate at position {0}")] 16 | NonIssuer(usize), 17 | } 18 | 19 | #[derive(Error, Debug)] 20 | #[non_exhaustive] 21 | pub enum Error { 22 | #[error("Cryptographic error stack: {0}")] 23 | CryptoStack(#[from] openssl::error::ErrorStack), 24 | #[error("Key derivation error: {0:?}")] 25 | Kdf(#[from] openssl_kdf::KdfError), 26 | #[error("Serialization error: {0}")] 27 | SerdeCborError(#[from] serde_cbor::Error), 28 | #[error("Deserialization error (ciborium): {0}")] 29 | CiboriumDeError(#[from] ciborium::de::Error), 30 | #[error("Serialization error (ciborium): {0}")] 31 | CiboriumSerError(#[from] ciborium::ser::Error), 32 | #[error("COSE error: {0}")] 33 | Cose(#[from] aws_nitro_enclaves_cose::error::CoseError), 34 | #[error("Invalid hash value")] 35 | IncorrectHash, 36 | #[error("Incorrect nonce value")] 37 | IncorrectNonce, 38 | #[error("Unsupported algorithm used")] 39 | UnsupportedAlgorithm, 40 | #[error("Non-owner key attempted to sign")] 41 | NonOwnerKey, 42 | #[error("Feature not implemented yet: {0}")] 43 | NotImplemented(&'static str), 44 | #[error("Inconsistent values were used for '{0}'")] 45 | InconsistentValue(&'static str), 46 | #[error("An invalid state machine transition was attempted")] 47 | InvalidTransition, 48 | #[error("Data structure with invalid protocol version {0} was encountered")] 49 | InvalidProtocolVersion(ProtocolVersion), 50 | #[error("Invalid cryptographic suite name requested: {0}")] 51 | InvalidSuiteName(String), 52 | #[error("Invalid entry number requested")] 53 | InvalidEntryNum, 54 | #[error("Error in key exchange: {0}")] 55 | KeyExchangeError(&'static str), 56 | #[error("Invalid certificate chain encountered: {0}")] 57 | InvalidChain(ChainError), 58 | #[error("Array parse error: {0}")] 59 | ArrayParseError(#[from] crate::cborparser::ArrayParseError), 60 | #[error("PEM parse error")] 61 | PemError(#[from] pem::PemError), 62 | #[error("Invalid PEM tag: {0}")] 63 | InvalidPemTag(String), 64 | #[error("I/O error")] 65 | IoError(#[from] std::io::Error), 66 | #[error("Error parsing hex value: {0}")] 67 | HexError(#[from] hex::FromHexError), 68 | #[error("Error parsing ip address: {0}")] 69 | AddrError(#[from] std::net::AddrParseError), 70 | #[error("Unsupported version structure encountered. Version: {0:?}")] 71 | UnsupportedVersion(Option), 72 | #[error("TPM/TSS error: {0:?}")] 73 | TssError(#[from] tss_esapi::Error), 74 | #[error("Empty data")] 75 | EmptyData, 76 | } 77 | -------------------------------------------------------------------------------- /data-formats/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod errors; 2 | pub use errors::Error; 3 | 4 | pub mod constants; 5 | pub use constants::ProtocolVersion; 6 | 7 | pub mod devicecredential; 8 | pub use crate::devicecredential::DeviceCredential; 9 | 10 | pub mod types; 11 | 12 | pub mod enhanced_types; 13 | 14 | pub mod ownershipvoucher; 15 | 16 | pub mod publickey; 17 | 18 | pub mod messages; 19 | 20 | pub mod cborparser; 21 | 22 | mod serializable; 23 | pub use serializable::DeserializableMany; 24 | pub use serializable::Serializable; 25 | pub use serializable::StoredItem; 26 | 27 | pub fn interoperable_kdf_available() -> bool { 28 | #[cfg(feature = "use_noninteroperable_kdf")] 29 | { 30 | false 31 | } 32 | #[cfg(not(feature = "use_noninteroperable_kdf"))] 33 | { 34 | if std::env::var("FORCE_NONINTEROPERABLE_KDF").is_ok() { 35 | log::warn!("Forcing the use of non-interoperable KDF via environment variable"); 36 | false 37 | } else { 38 | true 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /data-formats/src/messages/mod.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | use crate::{ 4 | constants::{ErrorCode, MessageType}, 5 | ProtocolVersion, Serializable, 6 | }; 7 | 8 | pub mod v11; 9 | 10 | pub trait ClientMessage: Message {} 11 | pub trait ServerMessage: Message {} 12 | 13 | #[derive(Debug, Error)] 14 | pub enum ParseError { 15 | #[error("Overall error: {0}")] 16 | Error(#[from] crate::Error), 17 | #[error("Invalid body")] 18 | InvalidBody, 19 | } 20 | 21 | #[derive(Debug, PartialEq, Eq)] 22 | pub enum EncryptionRequirement { 23 | MustBeEncrypted, 24 | MustNotBeEncrypted, 25 | } 26 | 27 | pub trait Message: Send + Serializable + Sized { 28 | fn protocol_version() -> ProtocolVersion; 29 | 30 | fn message_type() -> MessageType; 31 | 32 | fn is_valid_previous_message(message_type: Option) -> bool; 33 | 34 | fn encryption_requirement() -> Option; 35 | 36 | fn status_code() -> http::StatusCode { 37 | http::StatusCode::OK 38 | } 39 | 40 | fn to_response(&self) -> Vec { 41 | match self.serialize_data() { 42 | Ok(v) => v, 43 | Err(e) => { 44 | eprintln!("Error serializing response: {e:?}"); 45 | 46 | let errmsg = match Self::protocol_version() { 47 | ProtocolVersion::Version1_0 => v11::ErrorMessage::new( 48 | ErrorCode::InternalServerError, 49 | Self::message_type(), 50 | "Error serializing response".to_string(), 51 | 0, 52 | ), 53 | ProtocolVersion::Version1_1 => v11::ErrorMessage::new( 54 | ErrorCode::InternalServerError, 55 | Self::message_type(), 56 | "Error serializing response".to_string(), 57 | 0, 58 | ), 59 | }; 60 | serde_cbor::to_vec(&errmsg).expect("Error serializing error message") 61 | } 62 | } 63 | } 64 | } 65 | 66 | #[allow(clippy::crate_in_macro_def)] 67 | #[macro_export] 68 | macro_rules! simple_message_serializable { 69 | ($name:ident, $inner_type:ident) => { 70 | impl crate::Serializable for $name { 71 | fn serialize_to_writer(&self, writer: W) -> core::result::Result<(), crate::Error> 72 | where 73 | W: std::io::Write, 74 | { 75 | self.0.serialize_to_writer(writer) 76 | } 77 | 78 | fn deserialize_from_reader(reader: R) -> core::result::Result 79 | where 80 | R: std::io::Read, 81 | { 82 | Ok(Self($inner_type::deserialize_from_reader(reader)?)) 83 | } 84 | } 85 | }; 86 | } 87 | -------------------------------------------------------------------------------- /data-formats/src/messages/v10/di.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize, Serializer}; 2 | use serde_tuple::Serialize_tuple; 3 | 4 | use crate::simple_message_serializable; 5 | use crate::{ 6 | constants::MessageType, 7 | messages::{ClientMessage, EncryptionRequirement, Message, ServerMessage}, 8 | ownershipvoucher::OwnershipVoucherHeader, 9 | types::{CborSimpleType, HMac}, 10 | }; 11 | 12 | #[derive(Debug, Serialize_tuple, Deserialize)] 13 | pub struct AppStart { 14 | mfg_info: CborSimpleType, 15 | } 16 | 17 | impl AppStart { 18 | pub fn new(mfg_info: CborSimpleType) -> Self { 19 | AppStart { mfg_info } 20 | } 21 | 22 | pub fn mfg_info(&self) -> &CborSimpleType { 23 | &self.mfg_info 24 | } 25 | } 26 | 27 | impl Message for AppStart { 28 | fn message_type() -> MessageType { 29 | MessageType::DIAppStart 30 | } 31 | 32 | fn is_valid_previous_message(message_type: Option) -> bool { 33 | matches!(message_type, None | Some(MessageType::DIUNDone)) 34 | } 35 | 36 | fn encryption_requirement() -> Option { 37 | None 38 | } 39 | 40 | fn protocol_version() -> crate::ProtocolVersion { 41 | crate::ProtocolVersion::Version1_0 42 | } 43 | } 44 | 45 | impl ClientMessage for AppStart {} 46 | 47 | #[derive(Debug)] 48 | pub struct SetCredentials(OwnershipVoucherHeader); 49 | 50 | simple_message_serializable!(SetCredentials, OwnershipVoucherHeader); 51 | 52 | impl SetCredentials { 53 | pub fn new(ov_header: OwnershipVoucherHeader) -> Self { 54 | SetCredentials(ov_header) 55 | } 56 | 57 | pub fn ov_header(&self) -> &OwnershipVoucherHeader { 58 | &self.0 59 | } 60 | 61 | pub fn into_ov_header(self) -> OwnershipVoucherHeader { 62 | self.0 63 | } 64 | } 65 | 66 | impl Message for SetCredentials { 67 | fn message_type() -> MessageType { 68 | MessageType::DISetCredentials 69 | } 70 | 71 | fn is_valid_previous_message(message_type: Option) -> bool { 72 | matches!(message_type, Some(MessageType::DIAppStart)) 73 | } 74 | 75 | fn encryption_requirement() -> Option { 76 | None 77 | } 78 | 79 | fn protocol_version() -> crate::ProtocolVersion { 80 | crate::ProtocolVersion::Version1_0 81 | } 82 | } 83 | 84 | impl ServerMessage for SetCredentials {} 85 | 86 | #[derive(Debug, Serialize_tuple, Deserialize)] 87 | pub struct SetHMAC { 88 | hmac: HMac, 89 | } 90 | 91 | impl SetHMAC { 92 | pub fn new(hmac: HMac) -> Self { 93 | SetHMAC { hmac } 94 | } 95 | 96 | pub fn hmac(&self) -> &HMac { 97 | &self.hmac 98 | } 99 | } 100 | 101 | impl Message for SetHMAC { 102 | fn message_type() -> MessageType { 103 | MessageType::DISetHMAC 104 | } 105 | 106 | fn is_valid_previous_message(message_type: Option) -> bool { 107 | matches!(message_type, Some(MessageType::DISetCredentials)) 108 | } 109 | 110 | fn encryption_requirement() -> Option { 111 | None 112 | } 113 | 114 | fn protocol_version() -> crate::ProtocolVersion { 115 | crate::ProtocolVersion::Version1_0 116 | } 117 | } 118 | 119 | impl ClientMessage for SetHMAC {} 120 | 121 | #[derive(Debug, Deserialize)] 122 | pub struct Done {} 123 | 124 | #[allow(clippy::new_without_default)] 125 | impl Done { 126 | pub fn new() -> Self { 127 | Done {} 128 | } 129 | } 130 | 131 | impl Message for Done { 132 | fn message_type() -> MessageType { 133 | MessageType::DIDone 134 | } 135 | 136 | fn is_valid_previous_message(message_type: Option) -> bool { 137 | matches!(message_type, Some(MessageType::DISetHMAC)) 138 | } 139 | 140 | fn encryption_requirement() -> Option { 141 | None 142 | } 143 | 144 | fn protocol_version() -> crate::ProtocolVersion { 145 | crate::ProtocolVersion::Version1_0 146 | } 147 | } 148 | 149 | impl ServerMessage for Done {} 150 | 151 | // We can't use Serialize_tuple here because that doesn't work for empty things 152 | impl Serialize for Done { 153 | fn serialize(&self, serializer: S) -> Result 154 | where 155 | S: Serializer, 156 | { 157 | use serde::ser::SerializeSeq; 158 | let seq = serializer.serialize_seq(Some(0))?; 159 | seq.end() 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /data-formats/src/messages/v10/error.rs: -------------------------------------------------------------------------------- 1 | use serde_tuple::{Deserialize_tuple, Serialize_tuple}; 2 | 3 | use crate::{ 4 | constants::{ErrorCode, MessageType}, 5 | messages::{ClientMessage, EncryptionRequirement, Message, ServerMessage}, 6 | }; 7 | 8 | #[derive(Debug, Serialize_tuple, Deserialize_tuple)] 9 | pub struct ErrorMessage { 10 | error_code: ErrorCode, 11 | previous_message_type: MessageType, 12 | error_string: String, 13 | error_timestamp: Option, 14 | error_uuid: u128, 15 | } 16 | 17 | impl ErrorMessage { 18 | pub fn new( 19 | error_code: ErrorCode, 20 | previous_message_type: MessageType, 21 | error_string: String, 22 | error_uuid: u128, 23 | ) -> Self { 24 | ErrorMessage { 25 | error_code, 26 | previous_message_type, 27 | error_string, 28 | error_timestamp: None, 29 | error_uuid, 30 | } 31 | } 32 | 33 | pub fn error_code(&self) -> ErrorCode { 34 | self.error_code 35 | } 36 | 37 | pub fn previous_message_type(&self) -> MessageType { 38 | self.previous_message_type 39 | } 40 | 41 | pub fn error_string(&self) -> &str { 42 | &self.error_string 43 | } 44 | 45 | pub fn error_timestamp(&self) -> Option<&serde_cbor::Value> { 46 | self.error_timestamp.as_ref() 47 | } 48 | 49 | pub fn error_uuid(&self) -> u128 { 50 | self.error_uuid 51 | } 52 | } 53 | 54 | impl Message for ErrorMessage { 55 | fn message_type() -> MessageType { 56 | MessageType::Error 57 | } 58 | 59 | fn is_valid_previous_message(_message_type: Option) -> bool { 60 | true 61 | } 62 | 63 | fn encryption_requirement() -> Option { 64 | None 65 | } 66 | 67 | fn status_code() -> http::StatusCode { 68 | http::StatusCode::INTERNAL_SERVER_ERROR 69 | } 70 | 71 | fn protocol_version() -> crate::ProtocolVersion { 72 | crate::ProtocolVersion::Version1_0 73 | } 74 | } 75 | 76 | impl ClientMessage for ErrorMessage {} 77 | impl ServerMessage for ErrorMessage {} 78 | 79 | #[cfg(test)] 80 | mod test { 81 | use std::u128; 82 | 83 | use super::ErrorCode; 84 | use super::ErrorMessage; 85 | use super::MessageType; 86 | use crate::Serializable; 87 | 88 | #[test] 89 | fn test_error_message_serialization() { 90 | let error_code = ErrorCode::InvalidOwnershipVoucher; 91 | let previous_message_type = MessageType::TO0OwnerSign; 92 | let error_string = "Ownership voucher manufacturer not trusted".to_string(); 93 | let error_uuid = 16378777930150272023 as u128; 94 | let error = ErrorMessage::new(error_code, previous_message_type, error_string, error_uuid); 95 | 96 | let serialized_error = error.serialize_data(); 97 | assert!(serialized_error.is_ok()); 98 | let serialized_data = serialized_error.unwrap(); 99 | let serialized_string = hex::encode(serialized_data); 100 | // Check the error message is serialized as a CBOR array 101 | assert!(serialized_string.starts_with("85")); 102 | } 103 | #[test] 104 | fn test_error_message_deserialization() { 105 | let error_message_encoded = "850216782a4f776e65727368697020766f7563686572206d616e756661637475726572206e6f742074727573746564f61be34d1bbfbd9d5c17"; 106 | 107 | let error_code = ErrorCode::InvalidOwnershipVoucher; 108 | let previous_message_type = MessageType::TO0OwnerSign; 109 | let error_string = "Ownership voucher manufacturer not trusted".to_string(); 110 | let error_uuid = 16378777930150272023 as u128; 111 | 112 | let error_decoded = hex::decode(error_message_encoded); 113 | assert!(error_decoded.is_ok()); 114 | let mut error_bytes = error_decoded.unwrap(); 115 | let error_test = &error_bytes.as_mut_slice(); 116 | let error_deserialized = ErrorMessage::deserialize_data(error_test); 117 | assert!(error_deserialized.is_ok()); 118 | let error_message = error_deserialized.unwrap(); 119 | assert_eq!(error_message.error_code(), error_code); 120 | assert_eq!(error_message.previous_message_type(), previous_message_type); 121 | assert_eq!(error_message.error_string(), error_string); 122 | assert_eq!(error_message.error_uuid(), error_uuid); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /data-formats/src/messages/v10/mod.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | pub use error::ErrorMessage; 3 | 4 | pub mod di; 5 | pub mod diun; 6 | pub mod to0; 7 | pub mod to1; 8 | pub mod to2; 9 | -------------------------------------------------------------------------------- /data-formats/src/messages/v10/to0.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use serde_tuple::Serialize_tuple; 3 | 4 | use crate::{ 5 | cborparser::ParsedArray, 6 | constants::{HashType, MessageType}, 7 | messages::{ClientMessage, EncryptionRequirement, Message, ServerMessage}, 8 | types::{COSESign, Hash, Nonce, TO0Data}, 9 | Error, Serializable, 10 | }; 11 | 12 | #[derive(Debug, Deserialize)] 13 | pub struct Hello {} 14 | 15 | impl Hello { 16 | #[allow(clippy::new_without_default)] 17 | pub fn new() -> Self { 18 | Hello {} 19 | } 20 | } 21 | 22 | impl Message for Hello { 23 | fn message_type() -> MessageType { 24 | MessageType::TO0Hello 25 | } 26 | 27 | fn is_valid_previous_message(message_type: Option) -> bool { 28 | matches!(message_type, None) 29 | } 30 | 31 | fn encryption_requirement() -> Option { 32 | Some(EncryptionRequirement::MustNotBeEncrypted) 33 | } 34 | 35 | fn protocol_version() -> crate::ProtocolVersion { 36 | crate::ProtocolVersion::Version1_0 37 | } 38 | } 39 | 40 | impl Serialize for Hello { 41 | fn serialize(&self, serializer: S) -> Result 42 | where 43 | S: serde::Serializer, 44 | { 45 | use serde::ser::SerializeSeq; 46 | let seq = serializer.serialize_seq(Some(0))?; 47 | seq.end() 48 | } 49 | } 50 | 51 | impl ClientMessage for Hello {} 52 | 53 | #[derive(Debug, Serialize_tuple, Deserialize)] 54 | pub struct HelloAck { 55 | nonce3: Nonce, 56 | } 57 | 58 | impl HelloAck { 59 | pub fn new(nonce3: Nonce) -> Self { 60 | HelloAck { nonce3 } 61 | } 62 | 63 | pub fn nonce3(&self) -> &Nonce { 64 | &self.nonce3 65 | } 66 | } 67 | 68 | impl Message for HelloAck { 69 | fn message_type() -> MessageType { 70 | MessageType::TO0HelloAck 71 | } 72 | 73 | fn is_valid_previous_message(message_type: Option) -> bool { 74 | matches!(message_type, Some(MessageType::TO0Hello)) 75 | } 76 | 77 | fn encryption_requirement() -> Option { 78 | Some(EncryptionRequirement::MustNotBeEncrypted) 79 | } 80 | 81 | fn protocol_version() -> crate::ProtocolVersion { 82 | crate::ProtocolVersion::Version1_0 83 | } 84 | } 85 | 86 | impl ServerMessage for HelloAck {} 87 | 88 | #[derive(Debug)] 89 | pub struct OwnerSign { 90 | contents: ParsedArray, 91 | 92 | cached_to0d: TO0Data, 93 | cached_to1d: COSESign, 94 | } 95 | 96 | impl Serializable for OwnerSign { 97 | fn deserialize_from_reader(reader: R) -> Result 98 | where 99 | R: std::io::Read, 100 | { 101 | let contents: ParsedArray = 102 | ParsedArray::deserialize_from_reader(reader)?; 103 | let to0d = contents.get(0)?; 104 | let to1d = contents.get(1)?; 105 | 106 | Ok(OwnerSign { 107 | contents, 108 | 109 | cached_to0d: to0d, 110 | cached_to1d: to1d, 111 | }) 112 | } 113 | 114 | fn serialize_to_writer(&self, writer: W) -> Result<(), Error> 115 | where 116 | W: std::io::Write, 117 | { 118 | self.contents.serialize_to_writer(writer) 119 | } 120 | } 121 | 122 | impl OwnerSign { 123 | pub fn new(to0d: TO0Data, to1d: COSESign) -> Result { 124 | let mut contents = unsafe { ParsedArray::new() }; 125 | contents.set(0, &to0d)?; 126 | contents.set(1, &to1d)?; 127 | 128 | Ok(OwnerSign { 129 | contents, 130 | 131 | cached_to0d: to0d, 132 | cached_to1d: to1d, 133 | }) 134 | } 135 | 136 | pub fn to0d(&self) -> &TO0Data { 137 | &self.cached_to0d 138 | } 139 | 140 | pub fn to1d(&self) -> &COSESign { 141 | &self.cached_to1d 142 | } 143 | 144 | pub fn to0d_hash(&self, hash_type: HashType) -> Result { 145 | self.contents.get_hash(0, hash_type) 146 | } 147 | } 148 | 149 | impl Message for OwnerSign { 150 | fn message_type() -> MessageType { 151 | MessageType::TO0OwnerSign 152 | } 153 | 154 | fn is_valid_previous_message(message_type: Option) -> bool { 155 | matches!(message_type, Some(MessageType::TO0HelloAck)) 156 | } 157 | 158 | fn encryption_requirement() -> Option { 159 | Some(EncryptionRequirement::MustNotBeEncrypted) 160 | } 161 | 162 | fn protocol_version() -> crate::ProtocolVersion { 163 | crate::ProtocolVersion::Version1_0 164 | } 165 | } 166 | 167 | impl ClientMessage for OwnerSign {} 168 | 169 | #[derive(Debug, Serialize_tuple, Deserialize)] 170 | pub struct AcceptOwner { 171 | wait_seconds: u32, 172 | } 173 | 174 | impl AcceptOwner { 175 | pub fn new(wait_seconds: u32) -> Self { 176 | AcceptOwner { wait_seconds } 177 | } 178 | 179 | pub fn wait_seconds(&self) -> u32 { 180 | self.wait_seconds 181 | } 182 | } 183 | 184 | impl Message for AcceptOwner { 185 | fn message_type() -> MessageType { 186 | MessageType::TO0AcceptOwner 187 | } 188 | 189 | fn is_valid_previous_message(message_type: Option) -> bool { 190 | matches!(message_type, Some(MessageType::TO0OwnerSign)) 191 | } 192 | 193 | fn encryption_requirement() -> Option { 194 | Some(EncryptionRequirement::MustNotBeEncrypted) 195 | } 196 | 197 | fn protocol_version() -> crate::ProtocolVersion { 198 | crate::ProtocolVersion::Version1_0 199 | } 200 | } 201 | 202 | impl ServerMessage for AcceptOwner {} 203 | -------------------------------------------------------------------------------- /data-formats/src/messages/v10/to1.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use serde_tuple::Serialize_tuple; 3 | 4 | use crate::simple_message_serializable; 5 | use crate::{ 6 | constants::MessageType, 7 | messages::{ClientMessage, EncryptionRequirement, Message, ServerMessage}, 8 | types::{COSESign, Guid, Nonce, SigInfo}, 9 | }; 10 | 11 | #[derive(Debug, Serialize_tuple, Deserialize)] 12 | pub struct HelloRV { 13 | guid: Guid, 14 | a_signature_info: SigInfo, 15 | } 16 | 17 | impl HelloRV { 18 | pub fn new(guid: Guid, a_signature_info: SigInfo) -> Self { 19 | HelloRV { 20 | guid, 21 | a_signature_info, 22 | } 23 | } 24 | 25 | pub fn guid(&self) -> &Guid { 26 | &self.guid 27 | } 28 | 29 | pub fn a_signature_info(&self) -> &SigInfo { 30 | &self.a_signature_info 31 | } 32 | } 33 | 34 | impl Message for HelloRV { 35 | fn message_type() -> MessageType { 36 | MessageType::TO1HelloRV 37 | } 38 | 39 | fn is_valid_previous_message(message_type: Option) -> bool { 40 | matches!(message_type, None) 41 | } 42 | 43 | fn encryption_requirement() -> Option { 44 | Some(EncryptionRequirement::MustNotBeEncrypted) 45 | } 46 | 47 | fn protocol_version() -> crate::ProtocolVersion { 48 | crate::ProtocolVersion::Version1_0 49 | } 50 | } 51 | 52 | impl ClientMessage for HelloRV {} 53 | 54 | #[derive(Debug, Serialize_tuple, Deserialize)] 55 | pub struct HelloRVAck { 56 | nonce4: Nonce, 57 | b_signature_info: SigInfo, 58 | } 59 | 60 | impl HelloRVAck { 61 | pub fn new(nonce4: Nonce, b_signature_info: SigInfo) -> Self { 62 | HelloRVAck { 63 | nonce4, 64 | b_signature_info, 65 | } 66 | } 67 | 68 | pub fn nonce4(&self) -> &Nonce { 69 | &self.nonce4 70 | } 71 | 72 | pub fn b_signature_info(&self) -> &SigInfo { 73 | &self.b_signature_info 74 | } 75 | } 76 | 77 | impl Message for HelloRVAck { 78 | fn message_type() -> MessageType { 79 | MessageType::TO1HelloRVAck 80 | } 81 | 82 | fn is_valid_previous_message(message_type: Option) -> bool { 83 | matches!(message_type, Some(MessageType::TO1HelloRV)) 84 | } 85 | 86 | fn encryption_requirement() -> Option { 87 | Some(EncryptionRequirement::MustNotBeEncrypted) 88 | } 89 | 90 | fn protocol_version() -> crate::ProtocolVersion { 91 | crate::ProtocolVersion::Version1_0 92 | } 93 | } 94 | 95 | impl ServerMessage for HelloRVAck {} 96 | 97 | #[derive(Debug)] 98 | pub struct ProveToRV(COSESign); 99 | 100 | simple_message_serializable!(ProveToRV, COSESign); 101 | 102 | impl ProveToRV { 103 | pub fn new(token: COSESign) -> Self { 104 | ProveToRV(token) 105 | } 106 | 107 | pub fn token(&self) -> &COSESign { 108 | &self.0 109 | } 110 | } 111 | 112 | impl Message for ProveToRV { 113 | fn message_type() -> MessageType { 114 | MessageType::TO1ProveToRV 115 | } 116 | 117 | fn is_valid_previous_message(message_type: Option) -> bool { 118 | matches!(message_type, Some(MessageType::TO1HelloRVAck)) 119 | } 120 | 121 | fn encryption_requirement() -> Option { 122 | Some(EncryptionRequirement::MustNotBeEncrypted) 123 | } 124 | 125 | fn protocol_version() -> crate::ProtocolVersion { 126 | crate::ProtocolVersion::Version1_0 127 | } 128 | } 129 | 130 | impl ClientMessage for ProveToRV {} 131 | 132 | #[derive(Debug)] 133 | pub struct RVRedirect(COSESign); 134 | 135 | simple_message_serializable!(RVRedirect, COSESign); 136 | 137 | impl RVRedirect { 138 | pub fn new(to1d: COSESign) -> Self { 139 | RVRedirect(to1d) 140 | } 141 | 142 | pub fn to1d(&self) -> &COSESign { 143 | &self.0 144 | } 145 | 146 | pub fn into_to1d(self) -> COSESign { 147 | self.0 148 | } 149 | } 150 | 151 | impl Message for RVRedirect { 152 | fn message_type() -> MessageType { 153 | MessageType::TO1RVRedirect 154 | } 155 | 156 | fn is_valid_previous_message(message_type: Option) -> bool { 157 | matches!(message_type, Some(MessageType::TO1ProveToRV)) 158 | } 159 | 160 | fn encryption_requirement() -> Option { 161 | Some(EncryptionRequirement::MustNotBeEncrypted) 162 | } 163 | 164 | fn protocol_version() -> crate::ProtocolVersion { 165 | crate::ProtocolVersion::Version1_0 166 | } 167 | } 168 | 169 | impl ServerMessage for RVRedirect {} 170 | -------------------------------------------------------------------------------- /data-formats/src/messages/v11/di.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize, Serializer}; 2 | use serde_bytes::ByteBuf; 3 | use serde_tuple::Serialize_tuple; 4 | 5 | use crate::{ 6 | constants::MessageType, 7 | messages::{ClientMessage, EncryptionRequirement, Message, ServerMessage}, 8 | ownershipvoucher::OwnershipVoucherHeader, 9 | types::{CborSimpleType, HMac}, 10 | }; 11 | use crate::{simple_message_serializable, Error}; 12 | 13 | #[derive(Debug, Serialize_tuple, Deserialize)] 14 | pub struct AppStart { 15 | mfg_info: ByteBuf, 16 | } 17 | 18 | impl AppStart { 19 | pub fn new(mfg_info: CborSimpleType) -> Result { 20 | let mut buffer = Vec::new(); 21 | ciborium::ser::into_writer(&mfg_info, &mut buffer)?; 22 | Ok(AppStart { 23 | mfg_info: ByteBuf::from(buffer), 24 | }) 25 | } 26 | 27 | pub fn mfg_info(&self) -> Result { 28 | serde_cbor::from_slice(&self.mfg_info).map_err(Error::from) 29 | } 30 | } 31 | 32 | impl Message for AppStart { 33 | fn message_type() -> MessageType { 34 | MessageType::DIAppStart 35 | } 36 | 37 | fn is_valid_previous_message(message_type: Option) -> bool { 38 | matches!(message_type, None | Some(MessageType::DIUNDone)) 39 | } 40 | 41 | fn encryption_requirement() -> Option { 42 | None 43 | } 44 | 45 | fn protocol_version() -> crate::ProtocolVersion { 46 | crate::ProtocolVersion::Version1_1 47 | } 48 | } 49 | 50 | impl ClientMessage for AppStart {} 51 | 52 | #[derive(Debug)] 53 | pub struct SetCredentials(OwnershipVoucherHeader); 54 | 55 | simple_message_serializable!(SetCredentials, OwnershipVoucherHeader); 56 | 57 | impl SetCredentials { 58 | pub fn new(ov_header: OwnershipVoucherHeader) -> Self { 59 | SetCredentials(ov_header) 60 | } 61 | 62 | pub fn ov_header(&self) -> &OwnershipVoucherHeader { 63 | &self.0 64 | } 65 | 66 | pub fn into_ov_header(self) -> OwnershipVoucherHeader { 67 | self.0 68 | } 69 | } 70 | 71 | impl Message for SetCredentials { 72 | fn message_type() -> MessageType { 73 | MessageType::DISetCredentials 74 | } 75 | 76 | fn is_valid_previous_message(message_type: Option) -> bool { 77 | matches!(message_type, Some(MessageType::DIAppStart)) 78 | } 79 | 80 | fn encryption_requirement() -> Option { 81 | None 82 | } 83 | 84 | fn protocol_version() -> crate::ProtocolVersion { 85 | crate::ProtocolVersion::Version1_1 86 | } 87 | } 88 | 89 | impl ServerMessage for SetCredentials {} 90 | 91 | #[derive(Debug, Serialize_tuple, Deserialize)] 92 | pub struct SetHMAC { 93 | hmac: HMac, 94 | } 95 | 96 | impl SetHMAC { 97 | pub fn new(hmac: HMac) -> Self { 98 | SetHMAC { hmac } 99 | } 100 | 101 | pub fn hmac(&self) -> &HMac { 102 | &self.hmac 103 | } 104 | } 105 | 106 | impl Message for SetHMAC { 107 | fn message_type() -> MessageType { 108 | MessageType::DISetHMAC 109 | } 110 | 111 | fn is_valid_previous_message(message_type: Option) -> bool { 112 | matches!(message_type, Some(MessageType::DISetCredentials)) 113 | } 114 | 115 | fn encryption_requirement() -> Option { 116 | None 117 | } 118 | 119 | fn protocol_version() -> crate::ProtocolVersion { 120 | crate::ProtocolVersion::Version1_1 121 | } 122 | } 123 | 124 | impl ClientMessage for SetHMAC {} 125 | 126 | #[derive(Debug, Deserialize)] 127 | pub struct Done {} 128 | 129 | #[allow(clippy::new_without_default)] 130 | impl Done { 131 | pub fn new() -> Self { 132 | Done {} 133 | } 134 | } 135 | 136 | impl Message for Done { 137 | fn message_type() -> MessageType { 138 | MessageType::DIDone 139 | } 140 | 141 | fn is_valid_previous_message(message_type: Option) -> bool { 142 | matches!(message_type, Some(MessageType::DISetHMAC)) 143 | } 144 | 145 | fn encryption_requirement() -> Option { 146 | None 147 | } 148 | 149 | fn protocol_version() -> crate::ProtocolVersion { 150 | crate::ProtocolVersion::Version1_1 151 | } 152 | } 153 | 154 | impl ServerMessage for Done {} 155 | 156 | // We can't use Serialize_tuple here because that doesn't work for empty things 157 | impl Serialize for Done { 158 | fn serialize(&self, serializer: S) -> Result 159 | where 160 | S: Serializer, 161 | { 162 | use serde::ser::SerializeSeq; 163 | let seq = serializer.serialize_seq(Some(0))?; 164 | seq.end() 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /data-formats/src/messages/v11/error.rs: -------------------------------------------------------------------------------- 1 | use serde_tuple::{Deserialize_tuple, Serialize_tuple}; 2 | 3 | use crate::{ 4 | constants::{ErrorCode, MessageType}, 5 | messages::{ClientMessage, EncryptionRequirement, Message, ServerMessage}, 6 | }; 7 | 8 | #[derive(Debug, Serialize_tuple, Deserialize_tuple)] 9 | pub struct ErrorMessage { 10 | error_code: ErrorCode, 11 | previous_message_type: MessageType, 12 | error_string: String, 13 | error_timestamp: Option, 14 | error_uuid: u128, 15 | } 16 | 17 | impl ErrorMessage { 18 | pub fn new( 19 | error_code: ErrorCode, 20 | previous_message_type: MessageType, 21 | error_string: String, 22 | error_uuid: u128, 23 | ) -> Self { 24 | ErrorMessage { 25 | error_code, 26 | previous_message_type, 27 | error_string, 28 | error_timestamp: None, 29 | error_uuid, 30 | } 31 | } 32 | 33 | pub fn error_code(&self) -> ErrorCode { 34 | self.error_code 35 | } 36 | 37 | pub fn previous_message_type(&self) -> MessageType { 38 | self.previous_message_type 39 | } 40 | 41 | pub fn error_string(&self) -> &str { 42 | &self.error_string 43 | } 44 | 45 | pub fn error_timestamp(&self) -> Option<&serde_cbor::Value> { 46 | self.error_timestamp.as_ref() 47 | } 48 | 49 | pub fn error_uuid(&self) -> u128 { 50 | self.error_uuid 51 | } 52 | } 53 | 54 | impl Message for ErrorMessage { 55 | fn message_type() -> MessageType { 56 | MessageType::Error 57 | } 58 | 59 | fn is_valid_previous_message(_message_type: Option) -> bool { 60 | true 61 | } 62 | 63 | fn encryption_requirement() -> Option { 64 | None 65 | } 66 | 67 | fn status_code() -> http::StatusCode { 68 | http::StatusCode::INTERNAL_SERVER_ERROR 69 | } 70 | 71 | fn protocol_version() -> crate::ProtocolVersion { 72 | crate::ProtocolVersion::Version1_1 73 | } 74 | } 75 | 76 | impl ClientMessage for ErrorMessage {} 77 | impl ServerMessage for ErrorMessage {} 78 | 79 | #[cfg(test)] 80 | mod test { 81 | 82 | use super::ErrorCode; 83 | use super::ErrorMessage; 84 | use super::MessageType; 85 | use crate::Serializable; 86 | 87 | #[test] 88 | fn test_error_message_serialization() { 89 | let error_code = ErrorCode::InvalidOwnershipVoucher; 90 | let previous_message_type = MessageType::TO0OwnerSign; 91 | let error_string = "Ownership voucher manufacturer not trusted".to_string(); 92 | let error_uuid = 16378777930150272023_u128; 93 | let error = ErrorMessage::new(error_code, previous_message_type, error_string, error_uuid); 94 | 95 | let serialized_error = error.serialize_data(); 96 | assert!(serialized_error.is_ok()); 97 | let serialized_data = serialized_error.unwrap(); 98 | let serialized_string = hex::encode(serialized_data); 99 | // Check the error message is serialized as a CBOR array 100 | assert!(serialized_string.starts_with("85")); 101 | } 102 | #[test] 103 | fn test_error_message_deserialization() { 104 | let error_message_encoded = "850216782a4f776e65727368697020766f7563686572206d616e756661637475726572206e6f742074727573746564f61be34d1bbfbd9d5c17"; 105 | 106 | let error_code = ErrorCode::InvalidOwnershipVoucher; 107 | let previous_message_type = MessageType::TO0OwnerSign; 108 | let error_string = "Ownership voucher manufacturer not trusted".to_string(); 109 | let error_uuid = 16378777930150272023_u128; 110 | 111 | let error_decoded = hex::decode(error_message_encoded); 112 | assert!(error_decoded.is_ok()); 113 | let mut error_bytes = error_decoded.unwrap(); 114 | let error_test = &error_bytes.as_mut_slice(); 115 | let error_deserialized = ErrorMessage::deserialize_data(error_test); 116 | assert!(error_deserialized.is_ok()); 117 | let error_message = error_deserialized.unwrap(); 118 | assert_eq!(error_message.error_code(), error_code); 119 | assert_eq!(error_message.previous_message_type(), previous_message_type); 120 | assert_eq!(error_message.error_string(), error_string); 121 | assert_eq!(error_message.error_uuid(), error_uuid); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /data-formats/src/messages/v11/mod.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | pub use error::ErrorMessage; 3 | 4 | pub mod di; 5 | pub mod diun; 6 | pub mod to0; 7 | pub mod to1; 8 | pub mod to2; 9 | -------------------------------------------------------------------------------- /data-formats/src/messages/v11/to1.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use serde_tuple::Serialize_tuple; 3 | 4 | use crate::simple_message_serializable; 5 | use crate::{ 6 | constants::MessageType, 7 | messages::{ClientMessage, EncryptionRequirement, Message, ServerMessage}, 8 | types::{COSESign, Guid, Nonce, SigInfo}, 9 | }; 10 | 11 | #[derive(Debug, Serialize_tuple, Deserialize)] 12 | pub struct HelloRV { 13 | guid: Guid, 14 | a_signature_info: SigInfo, 15 | } 16 | 17 | impl HelloRV { 18 | pub fn new(guid: Guid, a_signature_info: SigInfo) -> Self { 19 | HelloRV { 20 | guid, 21 | a_signature_info, 22 | } 23 | } 24 | 25 | pub fn guid(&self) -> &Guid { 26 | &self.guid 27 | } 28 | 29 | pub fn a_signature_info(&self) -> &SigInfo { 30 | &self.a_signature_info 31 | } 32 | } 33 | 34 | impl Message for HelloRV { 35 | fn message_type() -> MessageType { 36 | MessageType::TO1HelloRV 37 | } 38 | 39 | fn is_valid_previous_message(message_type: Option) -> bool { 40 | message_type.is_none() 41 | } 42 | 43 | fn encryption_requirement() -> Option { 44 | Some(EncryptionRequirement::MustNotBeEncrypted) 45 | } 46 | 47 | fn protocol_version() -> crate::ProtocolVersion { 48 | crate::ProtocolVersion::Version1_1 49 | } 50 | } 51 | 52 | impl ClientMessage for HelloRV {} 53 | 54 | #[derive(Debug, Serialize_tuple, Deserialize)] 55 | pub struct HelloRVAck { 56 | nonce4: Nonce, 57 | b_signature_info: SigInfo, 58 | } 59 | 60 | impl HelloRVAck { 61 | pub fn new(nonce4: Nonce, b_signature_info: SigInfo) -> Self { 62 | HelloRVAck { 63 | nonce4, 64 | b_signature_info, 65 | } 66 | } 67 | 68 | pub fn nonce4(&self) -> &Nonce { 69 | &self.nonce4 70 | } 71 | 72 | pub fn b_signature_info(&self) -> &SigInfo { 73 | &self.b_signature_info 74 | } 75 | } 76 | 77 | impl Message for HelloRVAck { 78 | fn message_type() -> MessageType { 79 | MessageType::TO1HelloRVAck 80 | } 81 | 82 | fn is_valid_previous_message(message_type: Option) -> bool { 83 | matches!(message_type, Some(MessageType::TO1HelloRV)) 84 | } 85 | 86 | fn encryption_requirement() -> Option { 87 | Some(EncryptionRequirement::MustNotBeEncrypted) 88 | } 89 | 90 | fn protocol_version() -> crate::ProtocolVersion { 91 | crate::ProtocolVersion::Version1_1 92 | } 93 | } 94 | 95 | impl ServerMessage for HelloRVAck {} 96 | 97 | #[derive(Debug)] 98 | pub struct ProveToRV(COSESign); 99 | 100 | simple_message_serializable!(ProveToRV, COSESign); 101 | 102 | impl ProveToRV { 103 | pub fn new(token: COSESign) -> Self { 104 | ProveToRV(token) 105 | } 106 | 107 | pub fn token(&self) -> &COSESign { 108 | &self.0 109 | } 110 | } 111 | 112 | impl Message for ProveToRV { 113 | fn message_type() -> MessageType { 114 | MessageType::TO1ProveToRV 115 | } 116 | 117 | fn is_valid_previous_message(message_type: Option) -> bool { 118 | matches!(message_type, Some(MessageType::TO1HelloRVAck)) 119 | } 120 | 121 | fn encryption_requirement() -> Option { 122 | Some(EncryptionRequirement::MustNotBeEncrypted) 123 | } 124 | 125 | fn protocol_version() -> crate::ProtocolVersion { 126 | crate::ProtocolVersion::Version1_1 127 | } 128 | } 129 | 130 | impl ClientMessage for ProveToRV {} 131 | 132 | #[derive(Debug)] 133 | pub struct RVRedirect(COSESign); 134 | 135 | simple_message_serializable!(RVRedirect, COSESign); 136 | 137 | impl RVRedirect { 138 | pub fn new(to1d: COSESign) -> Self { 139 | RVRedirect(to1d) 140 | } 141 | 142 | pub fn to1d(&self) -> &COSESign { 143 | &self.0 144 | } 145 | 146 | pub fn into_to1d(self) -> COSESign { 147 | self.0 148 | } 149 | } 150 | 151 | impl Message for RVRedirect { 152 | fn message_type() -> MessageType { 153 | MessageType::TO1RVRedirect 154 | } 155 | 156 | fn is_valid_previous_message(message_type: Option) -> bool { 157 | matches!(message_type, Some(MessageType::TO1ProveToRV)) 158 | } 159 | 160 | fn encryption_requirement() -> Option { 161 | Some(EncryptionRequirement::MustNotBeEncrypted) 162 | } 163 | 164 | fn protocol_version() -> crate::ProtocolVersion { 165 | crate::ProtocolVersion::Version1_1 166 | } 167 | } 168 | 169 | impl ServerMessage for RVRedirect {} 170 | -------------------------------------------------------------------------------- /data-formats/src/serializable.rs: -------------------------------------------------------------------------------- 1 | use crate::cborparser::{ParsedArray, ParsedArrayBuilder, ParsedArraySize2}; 2 | use crate::publickey::PublicKey; 3 | use crate::types::COSESign; 4 | use crate::Error; 5 | 6 | pub trait Serializable { 7 | fn deserialize_data(data: &[u8]) -> Result 8 | where 9 | Self: Sized, 10 | { 11 | Self::deserialize_from_reader(data) 12 | } 13 | 14 | fn serialize_data(&self) -> Result, Error> { 15 | let mut output = Vec::new(); 16 | self.serialize_to_writer(&mut output)?; 17 | Ok(output) 18 | } 19 | 20 | fn deserialize_from_reader(reader: R) -> Result 21 | where 22 | Self: Sized, 23 | R: std::io::Read; 24 | 25 | fn serialize_to_writer(&self, writer: W) -> Result<(), Error> 26 | where 27 | W: std::io::Write; 28 | } 29 | 30 | pub trait MaybeSerializable: Serializable { 31 | fn is_nodata_error(err: &Error) -> bool; 32 | 33 | fn maybe_deserialize_from_reader(reader: R) -> Result, Error> 34 | where 35 | Self: Sized, 36 | R: std::io::Read, 37 | { 38 | match Self::deserialize_from_reader(reader) { 39 | Ok(value) => Ok(Some(value)), 40 | Err(err) => { 41 | if Self::is_nodata_error(&err) { 42 | Ok(None) 43 | } else { 44 | Err(err) 45 | } 46 | } 47 | } 48 | } 49 | } 50 | 51 | pub trait DeserializableMany: MaybeSerializable { 52 | fn deserialize_many_from_reader(mut reader: R) -> Result, Error> 53 | where 54 | Self: Sized, 55 | R: std::io::Read, 56 | { 57 | let mut output = Vec::new(); 58 | 59 | while let Some(item) = Self::maybe_deserialize_from_reader(&mut reader)? { 60 | output.push(item); 61 | } 62 | 63 | Ok(output) 64 | } 65 | } 66 | 67 | impl Serializable for T 68 | where 69 | T: serde::Serialize, 70 | T: serde::de::DeserializeOwned, 71 | { 72 | fn deserialize_data(data: &[u8]) -> Result { 73 | serde_cbor::from_slice(data).map_err(Error::from) 74 | } 75 | 76 | fn deserialize_from_reader(mut reader: R) -> Result 77 | where 78 | Self: Sized, 79 | R: std::io::Read, 80 | { 81 | serde_cbor::from_reader(&mut reader).map_err(Error::from) 82 | } 83 | 84 | fn serialize_to_writer(&self, mut writer: W) -> Result<(), Error> 85 | where 86 | W: std::io::Write, 87 | { 88 | ciborium::ser::into_writer(self, &mut writer).map_err(Error::from) 89 | } 90 | } 91 | 92 | #[derive(Clone, Debug)] 93 | pub struct StoredItem { 94 | pub public_key: PublicKey, 95 | pub to1d: COSESign, 96 | } 97 | 98 | impl Serializable for StoredItem { 99 | fn deserialize_from_reader(reader: R) -> Result 100 | where 101 | R: std::io::Read, 102 | { 103 | let contents: ParsedArray = ParsedArray::deserialize_from_reader(reader)?; 104 | 105 | let public_key = contents.get(0)?; 106 | let to1d = contents.get(1)?; 107 | 108 | Ok(StoredItem { public_key, to1d }) 109 | } 110 | 111 | fn serialize_to_writer(&self, writer: W) -> Result<(), Error> 112 | where 113 | W: std::io::Write, 114 | { 115 | let mut contents: ParsedArrayBuilder = ParsedArrayBuilder::new(); 116 | contents.set(0, &self.public_key)?; 117 | contents.set(1, &self.to1d)?; 118 | let contents = contents.build(); 119 | 120 | contents.serialize_to_writer(writer) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /db/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fdo-db" 3 | version = "0.5.5" 4 | edition = "2021" 5 | 6 | 7 | [dependencies] 8 | anyhow = "1.0" 9 | diesel = { version = "2.2.7", features = ["sqlite", "postgres", "r2d2"] } 10 | 11 | fdo-data-formats = { path = "../data-formats", version = "0.5.5" } 12 | 13 | [dev-dependencies] 14 | fdo-http-wrapper = { path = "../http-wrapper", version = "0.5.2", features = ["server"] } 15 | openssl = "0.10.72" 16 | 17 | [features] 18 | postgres = [] 19 | sqlite = [] 20 | 21 | default = ["postgres", "sqlite"] 22 | -------------------------------------------------------------------------------- /db/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod models; 2 | #[cfg(feature = "postgres")] 3 | pub mod postgres; 4 | pub mod schema; 5 | #[cfg(feature = "sqlite")] 6 | pub mod sqlite; 7 | 8 | use anyhow::Result; 9 | use diesel::r2d2::ConnectionManager; 10 | use diesel::r2d2::Pool; 11 | 12 | use fdo_data_formats::ownershipvoucher::OwnershipVoucher as OV; 13 | use fdo_data_formats::StoredItem; 14 | use models::ManufacturerOV; 15 | use models::OwnerOV; 16 | use models::RendezvousOV; 17 | 18 | pub trait DBStoreManufacturer 19 | where 20 | T: diesel::r2d2::R2D2Connection + 'static, 21 | { 22 | /// Gets a connection pool 23 | fn get_conn_pool(url: String) -> Pool>; 24 | 25 | /// Inserts an OV 26 | fn insert_ov(ov: &OV, ttl: Option, conn: &mut T) -> Result<()>; 27 | 28 | /// Gets an OV 29 | fn get_ov(guid: &str, conn: &mut T) -> Result; 30 | 31 | /// Returns all the OVs in the DB 32 | fn get_all_ovs(conn: &mut T) -> Result>; 33 | 34 | /// Deletes an OV 35 | fn delete_ov(guid: &str, conn: &mut T) -> Result<()>; 36 | 37 | /// Deletes all OVs whose ttl is less or equal to the given ttl 38 | fn delete_ov_ttl_le(ttl: i64, conn: &mut T) -> Result<()>; 39 | 40 | /// Updates the ttl of an existing OV. 41 | /// Option is set as the ttl type so that we can set NULL in the 42 | /// database if 'None' is passed as the ttl. 43 | fn update_ov_ttl(guid: &str, ttl: Option, conn: &mut T) -> Result<()>; 44 | } 45 | 46 | pub trait DBStoreOwner 47 | where 48 | T: diesel::r2d2::R2D2Connection + 'static, 49 | { 50 | /// Gets a connection pool 51 | fn get_conn_pool(url: String) -> Pool>; 52 | 53 | /// Inserts an OV 54 | fn insert_ov(ov: &OV, to2: Option, to0: Option, conn: &mut T) -> Result<()>; 55 | 56 | /// Gets an OV 57 | fn get_ov(guid: &str, conn: &mut T) -> Result; 58 | 59 | /// Returns all the OVs in the DB 60 | fn get_all_ovs(conn: &mut T) -> Result>; 61 | 62 | /// Deletes an OV 63 | fn delete_ov(guid: &str, conn: &mut T) -> Result<()>; 64 | 65 | /// Selects all the OVs with the given to2_performed status 66 | fn select_ov_to2_performed(to2_performed: bool, conn: &mut T) -> Result>; 67 | 68 | /// Selects all the OVs whose to0 is less than the given maximum 69 | fn select_ov_to0_less_than(to0_max: i64, conn: &mut T) -> Result>; 70 | 71 | /// Selects all the OVs with the given to2_performed status and those whose 72 | /// to0 is less that then given maximum 73 | fn select_ov_to2_performed_and_ov_to0_less_than( 74 | to2_performed: bool, 75 | to0_max: i64, 76 | conn: &mut T, 77 | ) -> Result>; 78 | 79 | /// Updates the to0_accept_owner_wait_seconds field of an existing OV. 80 | /// Option is set as the ttl type so that we can set NULL in the 81 | /// database if 'None' is passed as the value. 82 | fn update_ov_to0_wait_seconds( 83 | guid: &str, 84 | wait_seconds: Option, 85 | conn: &mut T, 86 | ) -> Result<()>; 87 | 88 | /// Updates the to0 performed status of an existing OV. 89 | /// Option is set as the ttl type so that we can set NULL in the 90 | /// database if 'None' is passed as the to0_performed 91 | fn update_ov_to2(guid: &str, to0_performed: Option, conn: &mut T) -> Result<()>; 92 | } 93 | 94 | pub trait DBStoreRendezvous 95 | where 96 | T: diesel::r2d2::R2D2Connection + 'static, 97 | { 98 | /// Gets a connection pool 99 | fn get_conn_pool(url: String) -> Pool>; 100 | 101 | /// Inserts an OV 102 | fn insert_ov(ov: &StoredItem, guid: &str, ttl: Option, conn: &mut T) -> Result<()>; 103 | 104 | /// Gets an OV 105 | fn get_ov(guid: &str, conn: &mut T) -> Result; 106 | 107 | /// Returns all the OVs in the DB 108 | fn get_all_ovs(conn: &mut T) -> Result>; 109 | 110 | /// Deletes an OV 111 | fn delete_ov(guid: &str, conn: &mut T) -> Result<()>; 112 | 113 | /// Deletes all OVs whose ttl is less or equal to the given ttl 114 | fn delete_ov_ttl_le(ttl: i64, conn: &mut T) -> Result<()>; 115 | 116 | /// Updates the ttl of an existing OV. 117 | /// Option is set as the ttl type so that we can set NULL in the 118 | /// database if 'None' is passed as the ttl. 119 | fn update_ov_ttl(guid: &str, ttl: Option, conn: &mut T) -> Result<()>; 120 | } 121 | -------------------------------------------------------------------------------- /db/src/models.rs: -------------------------------------------------------------------------------- 1 | use diesel::prelude::*; 2 | use std::fmt; 3 | 4 | #[derive(Queryable, Selectable, Identifiable)] 5 | #[diesel(table_name = crate::schema::rendezvous_vouchers)] 6 | #[diesel(treat_none_as_null = true)] 7 | #[diesel(primary_key(guid))] 8 | pub struct RendezvousOV { 9 | pub guid: String, 10 | pub contents: Vec, 11 | pub ttl: Option, 12 | } 13 | 14 | #[derive(Insertable)] 15 | #[diesel(table_name = crate::schema::rendezvous_vouchers)] 16 | pub struct NewRendezvousOV { 17 | pub guid: String, 18 | pub contents: Vec, 19 | pub ttl: Option, 20 | } 21 | 22 | #[derive(Queryable, Selectable, Identifiable, AsChangeset)] 23 | #[diesel(table_name = crate::schema::owner_vouchers)] 24 | #[diesel(treat_none_as_null = true)] 25 | #[diesel(primary_key(guid))] 26 | pub struct OwnerOV { 27 | pub guid: String, 28 | pub contents: Vec, 29 | pub to2_performed: Option, 30 | pub to0_accept_owner_wait_seconds: Option, 31 | } 32 | 33 | #[derive(Insertable)] 34 | #[diesel(table_name = crate::schema::owner_vouchers)] 35 | pub struct NewOwnerOV { 36 | pub guid: String, 37 | pub contents: Vec, 38 | pub to2_performed: Option, 39 | pub to0_accept_owner_wait_seconds: Option, 40 | } 41 | 42 | #[derive(Queryable, Selectable, Identifiable, AsChangeset)] 43 | #[diesel(table_name = crate::schema::manufacturer_vouchers)] 44 | #[diesel(treat_none_as_null = true)] 45 | #[diesel(primary_key(guid))] 46 | pub struct ManufacturerOV { 47 | pub guid: String, 48 | pub contents: Vec, 49 | pub ttl: Option, 50 | } 51 | 52 | #[derive(Insertable)] 53 | #[diesel(table_name = crate::schema::manufacturer_vouchers)] 54 | pub struct NewManufacturerOV { 55 | pub guid: String, 56 | pub contents: Vec, 57 | pub ttl: Option, 58 | } 59 | 60 | impl fmt::Display for RendezvousOV { 61 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 62 | write!( 63 | f, 64 | "GUID: {}, ttl: {:?}, contents: {:?}", 65 | self.guid, self.ttl, self.contents 66 | ) 67 | } 68 | } 69 | 70 | impl fmt::Display for OwnerOV { 71 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 72 | write!( 73 | f, 74 | "GUID: {}, to2_performed: {:?}, to0_accept_owner_wait_seconds {:?}, contents: {:?}", 75 | self.guid, self.to2_performed, self.to0_accept_owner_wait_seconds, self.contents 76 | ) 77 | } 78 | } 79 | 80 | impl fmt::Display for ManufacturerOV { 81 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 82 | write!( 83 | f, 84 | "GUID: {}, ttl: {:?}, contents: {:?}", 85 | self.guid, self.ttl, self.contents 86 | ) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /db/src/schema.rs: -------------------------------------------------------------------------------- 1 | diesel::table! { 2 | manufacturer_vouchers (guid) { 3 | guid -> Text, 4 | contents -> Binary, 5 | ttl -> Nullable, 6 | } 7 | } 8 | 9 | diesel::table! { 10 | owner_vouchers (guid) { 11 | guid -> Text, 12 | contents -> Binary, 13 | to2_performed -> Nullable, 14 | to0_accept_owner_wait_seconds -> Nullable, 15 | } 16 | } 17 | 18 | diesel::table! { 19 | rendezvous_vouchers (guid) { 20 | guid -> Text, 21 | contents -> Binary, 22 | ttl -> Nullable, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs-rpms/fdo-admin-cli.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | FDO-ADMIN-TOOL 3 | ============== 4 | 5 | FDO admin tools implementation. 6 | 7 | Please visit https://github.com/fedora-iot/fido-device-onboard-rs 8 | -------------------------------------------------------------------------------- /docs-rpms/fdo-client.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | FDO-CLIENT 3 | ========== 4 | 5 | FDO Client implementation. 6 | 7 | Please visit https://github.com/fedora-iot/fido-device-onboard-rs 8 | -------------------------------------------------------------------------------- /docs-rpms/fdo-init.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | FDO-INIT 3 | ============== 4 | 5 | dracut module for device initialization. 6 | 7 | Please visit https://github.com/fedora-iot/fido-device-onboard-rs 8 | -------------------------------------------------------------------------------- /docs-rpms/fdo-manufacturing-server.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | FDO-MANUFACTURING-SERVER 3 | ======================== 4 | 5 | FDO Manufacturing Server implementation. 6 | 7 | Please visit https://github.com/fedora-iot/fido-device-onboard-rs 8 | -------------------------------------------------------------------------------- /docs-rpms/fdo-owner-cli.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | FDO-OWNER-CLI 3 | ============== 4 | 5 | FDO Owner tools implementation 6 | 7 | Please visit https://github.com/fedora-iot/fido-device-onboard-rs 8 | -------------------------------------------------------------------------------- /docs-rpms/fdo-owner-onboarding-server.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | FDO-OWNER-ONBOARDING-SERVER 3 | =========================== 4 | 5 | FDO Owner Onboarding Server implementation. 6 | 7 | Please visit https://github.com/fedora-iot/fido-device-onboard-rs 8 | -------------------------------------------------------------------------------- /docs-rpms/fdo-rendezvous-server.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | FDO-RENDEZVOUS-SERVER 3 | ===================== 4 | 5 | FDO Rendezvous Server implementation. 6 | 7 | Please visit https://github.com/fedora-iot/fido-device-onboard-rs 8 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gem "github-pages" 3 | gem "jekyll-remote-theme" 4 | gem "just-the-docs" 5 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | title: "FIDO Device Onboarding Rust implementation" 2 | remote_theme: pmarsceill/just-the-docs 3 | color_scheme: "dark" 4 | search_enabled: false 5 | 6 | plugins: 7 | - jekyll-remote-theme 8 | - jekyll-seo-tag 9 | 10 | aux_links: 11 | "FIDO Device Onboard Rust on GitHub": 12 | - "https://github.com/fedora-iot/fido-device-onboard-rs/" 13 | aux_links_new_tab: true 14 | 15 | heading_anchors: true 16 | 17 | gh_edit_link: true 18 | gh_edit_link_text: "Edit this page on GitHub." 19 | gh_edit_repository: "https://github.com/fedora-iot/fido-device-onboard-rs" 20 | gh_edit_branch: "main" 21 | gh_edit_source: "docs" 22 | gh_edit_view_mode: "tree" 23 | 24 | # Non-changeable (GH Pages) 25 | lsi: false 26 | incremental: false 27 | highlighter: rouge 28 | gist: 29 | noscript: false 30 | kramdown: 31 | math_engine: mathjax 32 | syntax_highlighter: rouge 33 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | title: Home 4 | --- 5 | 6 | ## FIDO Device Onboarding Rust 7 | 8 | This is an open source implementation of the [FIDO Device Onboard Specification](https://fidoalliance.org/specs/FDO/fido-device-onboard-v1.0-ps-20210323/fido-device-onboard-v1.0-ps-20210323.html), written in Rust. 9 | -------------------------------------------------------------------------------- /docs/sims/encryption.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Full-Disk-Encryption configuration 4 | parent: ServiceInfo Modules 5 | --- 6 | 7 | ## ServiceInfo Module for configuration full disk encryption 8 | 9 | **STATUS: Draft** 10 | 11 | This document specifies a Service-Info Module to be used for configuring Full-Disk Encryption (FDE) on the device filesystem, in this case using the Linux LUKS2 mechanism and binding the passphrase to Clevis. 12 | 13 | The module name of this module during the TO2 protocol is *org.fedoraiot.diskencryption-clevis*. 14 | 15 | ### Owner Onboard Server to Device 16 | 17 | | messageName | Value Type | Value/Message Meaning | 18 | | --- | --- | --- | 19 | | `disk-label` | `tstr` | The filesystem/Device Mapper label of the disk | 20 | | `pin` | `tstr` | The name of the Clevis PIN | 21 | | `config` | `tstr` | The JSON-encoded configuration for Clevis | 22 | | `reencrypt` | `bool` | Whether or not to re-encrypt the disk | 23 | | `execute` | `null` | This message triggers the initiation of the re-encryption and binding procedures | 24 | 25 | ### Device to Owner Onboard Server 26 | 27 | | messageName | Value Type | Value/Message Meaning | 28 | | --- | --- | --- | 29 | | `disk-label` | `tstr` | The filesystem/Device Mapper label of the disk | 30 | | `reencrypt-initiated` | `bool` | Indicates whether a re-encrypt has been initiated | 31 | | `bound` | `bool` | Indicates whether rebinding to the new Clevis PIN completed successfully | 32 | -------------------------------------------------------------------------------- /docs/sims/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: ServiceInfo Modules 4 | has_children: true 5 | --- 6 | 7 | ## Service-Info Modules 8 | 9 | This section documents Service Info Modules that are not yet standardized at the FIDO level. 10 | -------------------------------------------------------------------------------- /docs/specs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Specifications 4 | has_children: true 5 | --- 6 | 7 | ## Specifications 8 | 9 | As part of this implementation, some additional APIs and protocols have been specified, and this directory contains their specification documents. 10 | -------------------------------------------------------------------------------- /docs/specs/serviceinfo_api.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Service Info API 4 | parent: Specifications 5 | --- 6 | 7 | ## Service Info API 8 | 9 | **STATUS: Draft** 10 | 11 | This specification describes a protocol that is used by the Owner Onboarding Service to retrieve information provided to the Device as part of the TO2 protocol. 12 | This means that the Owner Onboarding Service, during the ServiceInfo phase of Transfer Ownership 2, will reach out to a server via this API to retrieve the information to provision on the device. 13 | 14 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL 15 | NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and 16 | "OPTIONAL" in this document are to be interpreted as described in 17 | RFC 2119. 18 | 19 | ### General Features 20 | 21 | #### Encoding 22 | 23 | Requests and responses for the Management API use JSON encoding, instead of the CBOR encoding used throughout the FIDO specification. 24 | 25 | #### Authentication 26 | 27 | For this protocol, the FDO Owner Onboarding Server will authenticate to the server of this API, via any of the authentication methods defined in the [OV Management API specification](./ov_management_api.md#authentication). 28 | 29 | ### Endpoint 30 | 31 | This API consists of a single endpoint, the URL of which is configurable. 32 | The URL will have configurable fields for the api version, device GUID, and the list of supported modules. 33 | The API version for this specification is `1`. 34 | The modules are comma-separated. 35 | 36 | Whether a response is deemed successful is determined purely by the HTTP status code. 37 | If any status other than 200 is returned, the Owner Onboarding Server will cancel the onboarding procedure, and the device will retry later. 38 | 39 | A successful response will be a JSON object. 40 | The server can send as many keys of the following list as it has available: every key is optional. 41 | Additionally, the server is free to send more keys, and the FDO Owner Onboarding Server will ignore any keys that it does not recognize. 42 | 43 | #### Supported response keys 44 | 45 | The following keys are defined for the response to this endpoint: 46 | 47 | - `com.redhat.subscription_identity_certificate`: a string containing a PEM-encoded identity certificate for the system. 48 | - `initial_user`: a JSON object containing information about an initial user to be configured. Supported sub-keys are: 49 | - `username`: the username of the user to configure. 50 | - `ssh_keys`: a list of strings containing SSH keys to configure for this user. 51 | - `extra_commands`: a JSON list containing additional ServiceInfo commands that the Owner Onboarding Server does not have special support for. For further explanation, see the next section. 52 | 53 | ##### `extra_commands` 54 | 55 | The `extra_commands` list can be used to send ServiceInfo commands to the Device that the Owner Onboarding Server does not have explicit support for. 56 | 57 | The entries of this list are lists themselves, with the following fields: 58 | 59 | - Module name: JSON string 60 | - Command name: JSON string 61 | - Value: any JSON value 62 | 63 | There is one special handling of this: if the `command` value ends in `|hex`, the value should be a hex-encoded string, which will be converted to binary data before being sent to the Device. 64 | This is to overcome the lack of support for binary strings in JSON. 65 | 66 | #### Examples 67 | 68 | This assumes the URL is configured as `/device_info?serviceinfo_api_version=*api_version*&device_guid=*device_guid*&modules=*modules*`. 69 | 70 | ##### Request 71 | 72 | ``` HTTP 73 | GET /device_info?serviceinfo_api_version=1&device_guid=ab9dee81-65d4-40f4-9844-ed4208fbd852&modules=devmod,com.example.sshkey 74 | Host: deviceinfo.example.com 75 | User-Agent: FDO-Owner-Onboarding-Server/1.0 76 | Authorization: Bearer some-token-here 77 | Accept: application/json 78 | ``` 79 | 80 | ##### Successful response 81 | 82 | The JSON in this example is broken up in multiple lines for visibility, the server should handle either with or without this breaking. 83 | 84 | ``` HTTP 85 | HTTP/1.1 200 OK 86 | Content-Type: application/json 87 | Server: FDO-ServiceInfo-Server/1.0 88 | 89 | { 90 | "com.redhat.subscription_identity_certificate": "-----BEGIN CERTIFICATE-----\n......\n-----END CERTIFICATE-----", 91 | "initial_user": { 92 | "username": "root", 93 | "ssh_keys": ["ssh-rsa ...."] 94 | }, 95 | "undefined": "something_ignored", 96 | "extra_commands": [ 97 | ["binaryfile", "active", "true"], 98 | ["binaryfile", "name", "/etc/foo"], 99 | ["binaryfile", "length", 40], 100 | ["binaryfile", "mode", "0644"], 101 | ["binaryfile", "data001|hex", "39582abcd...."], 102 | ["binaryfile", "sha-384|hex", "48204bdef...."], 103 | ["command", "active", "true"], 104 | ["command", "command", "/usr/bin/touch"], 105 | ["command", "args", ["/etc/bar"]], 106 | ... 107 | ] 108 | } 109 | ``` 110 | -------------------------------------------------------------------------------- /dracut/52fdo/manufacturing-client-generator: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- 3 | # ex: ts=8 sw=4 sts=4 et filetype=sh 4 | 5 | command -v getarg >/dev/null || . /usr/lib/dracut-lib.sh 6 | 7 | set -e 8 | 9 | # Generators don't have logging right now 10 | # https://github.com/systemd/systemd/issues/15638 11 | exec 1>/dev/kmsg; exec 2>&1 12 | 13 | # if we're not running in dracut, skip 14 | if [ -z "$(getarg fdo.manufacturing_server_url)" ]; then 15 | exit 0 16 | fi 17 | 18 | manufacturing_server_url=$(getarg fdo.manufacturing_server_url= ||:) 19 | diun_pub_key_insecure=$(getarg fdo.diun_pub_key_insecure= ||:) 20 | diun_pub_key_hash=$(getarg fdo.diun_pub_key_hash= ||:) 21 | diun_pub_key_root_certs=$(getarg fdo.diun_pub_key_root_certs= ||:) 22 | mfg_string_type_mac_iface=$(getarg fdo.di_mfg_string_type_mac_iface= ||:) 23 | cat >"/run/manufacturing-client-config" <>"/run/manufacturing-client-config" <>"/run/manufacturing-client-config" <>"/run/manufacturing-client-config" <>"/run/manufacturing-client-config" <"/bootmount/fdo-client-env" <"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | futures = "0.3" 11 | thiserror = "1" 12 | async-trait = "0.1" 13 | log = "0.4" 14 | serde = { version = "1", features = ["derive"] } 15 | pretty_env_logger = "0.5" 16 | 17 | hex = "0.4" 18 | 19 | openssl = "0.10.72" 20 | 21 | fdo-data-formats = { path = "../data-formats", version = "0.5.5" } 22 | fdo-store = { path = "../store", version = "0.5.5" } 23 | aws-nitro-enclaves-cose = "0.5.2" 24 | 25 | # Server-side 26 | uuid = { version = "1.3", features = ["v4"], optional = true } 27 | warp = { version = "0.3.6", optional = true } 28 | warp-sessions = { version = "1.0", optional = true } 29 | time = "0.3" 30 | 31 | # Client-side 32 | reqwest = { version = "0.12", optional = true, features = ["native-tls", "json"] } 33 | url = { version = "2", optional = true } 34 | 35 | [features] 36 | server = ["warp", "warp-sessions", "uuid"] 37 | client = ["reqwest", "url"] 38 | -------------------------------------------------------------------------------- /http-wrapper/src/lib.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use aws_nitro_enclaves_cose::crypto::Openssl; 4 | use aws_nitro_enclaves_cose::error::CoseError; 5 | use aws_nitro_enclaves_cose::{CipherConfiguration, CoseEncrypt0}; 6 | use fdo_data_formats::types::{CipherSuite, DerivedKeys}; 7 | 8 | #[cfg(feature = "server")] 9 | pub mod server; 10 | 11 | #[cfg(feature = "client")] 12 | pub mod client; 13 | 14 | pub fn init_logging() { 15 | let filter = std::env::var("LOG_LEVEL").unwrap_or_else(|_| "info".to_string()); 16 | pretty_env_logger::formatted_timed_builder() 17 | .filter_level(log::LevelFilter::Info) 18 | .parse_filters(&filter) 19 | .init(); 20 | } 21 | 22 | #[derive(Debug, Serialize, Deserialize)] 23 | pub struct EncryptionKeys { 24 | cipher_suite: Option, 25 | keys: Option, 26 | } 27 | 28 | impl EncryptionKeys { 29 | pub fn unencrypted() -> Self { 30 | EncryptionKeys { 31 | cipher_suite: None, 32 | keys: None, 33 | } 34 | } 35 | 36 | pub fn is_none(&self) -> bool { 37 | self.cipher_suite.is_none() || self.keys.is_none() 38 | } 39 | 40 | pub fn is_some(&self) -> bool { 41 | !self.is_none() 42 | } 43 | 44 | pub fn from_derived(cipher_suite: CipherSuite, derived_keys: DerivedKeys) -> Self { 45 | EncryptionKeys { 46 | cipher_suite: Some(cipher_suite), 47 | keys: Some(derived_keys), 48 | } 49 | } 50 | 51 | #[allow(clippy::panic)] 52 | fn encrypt(&self, plaintext: &[u8]) -> Result, CoseError> { 53 | if self.cipher_suite.is_none() { 54 | Ok(plaintext.to_vec()) 55 | } else { 56 | let k = match &self.keys { 57 | Some(DerivedKeys::Combined { sevk: k }) => k, 58 | _ => panic!(), 59 | }; 60 | CoseEncrypt0::new::(plaintext, CipherConfiguration::Gcm, &k[..]) 61 | .map(|c| c.as_bytes(true))? 62 | } 63 | } 64 | 65 | #[allow(clippy::panic)] 66 | fn decrypt(&self, ciphertext: &[u8]) -> Result, CoseError> { 67 | if self.cipher_suite.is_none() { 68 | Ok(ciphertext.to_vec()) 69 | } else { 70 | let k = match &self.keys { 71 | Some(DerivedKeys::Combined { sevk: k }) => k, 72 | _ => panic!(), 73 | }; 74 | match CoseEncrypt0::from_bytes(ciphertext) { 75 | Ok(v) => match v.decrypt::(k) { 76 | Ok((_, _, payload)) => Ok(payload), 77 | Err(e) => Err(e), 78 | }, 79 | Err(e) => Err(e), 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /integration-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "integration-tests" 3 | version = "0.5.5" 4 | edition = "2018" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "test-locator" 9 | path = "locator.rs" 10 | 11 | [[test]] 12 | name = "di_diun-tests" 13 | path = "tests/di_diun.rs" 14 | 15 | [[test]] 16 | name = "to-tests" 17 | path = "tests/to.rs" 18 | 19 | [dev-dependencies] 20 | anyhow = "1" 21 | hex = "0.4" 22 | tempfile = "3" 23 | tera = "1" 24 | regex = "1.11.1" 25 | lazy_static = "1.4.0" 26 | openssl = "0.10.72" 27 | libc = "0.2" 28 | reqwest = { version = "0.12", features = ["json"] } 29 | tokio = "1.45.1" 30 | serde = "1" 31 | serde_cbor = "0.11" 32 | serde_json = "1.0" 33 | pretty_assertions = "1.0.0" 34 | paste = "1.0" 35 | pem = "3.0" 36 | 37 | fdo-data-formats = { path = "../data-formats" } 38 | fdo-util = { path = "../util" } 39 | -------------------------------------------------------------------------------- /integration-tests/locator.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /integration-tests/templates/manufacturing-server.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | session_store_driver: 3 | Directory: 4 | path: {{ config_dir }}/sessions/ 5 | ownership_voucher_store_driver: 6 | Directory: 7 | path: {{ test_dir }}/ownership_vouchers/ 8 | public_key_store_driver: 9 | Directory: 10 | path: {{ config_dir }}/keys/ 11 | bind: {{ bind }} 12 | rendezvous_info: 13 | - dns: localhost 14 | device_port: 8082 15 | owner_port: 8082 16 | protocol: http 17 | - dns: localhost 18 | delay: 30 19 | device_port: {{ rendezvous_port }} 20 | owner_port: {{ rendezvous_port }} 21 | protocol: http 22 | protocols: 23 | diun: 24 | key_path: {{ keys_path }}/diun_key.der 25 | cert_path: {{ keys_path }}/diun_cert.pem 26 | key_type: SECP256R1 27 | mfg_string_type: {{ device_identification_format }} 28 | allowed_key_storage_types: 29 | - {{ diun_key_type }} 30 | manufacturing: 31 | manufacturer_cert_path: {{ keys_path }}/manufacturer_cert.pem 32 | manufacturer_private_key: {{ keys_path }}/manufacturer_key.der 33 | owner_cert_path: {{ keys_path }}/owner_cert.pem 34 | device_cert_ca_private_key: {{ keys_path }}/device_ca_key.der 35 | device_cert_ca_chain: {{ keys_path }}/device_ca_cert.pem 36 | -------------------------------------------------------------------------------- /integration-tests/templates/owner-onboarding-server.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | session_store_driver: 3 | Directory: 4 | path: {{ config_dir }}/sessions/ 5 | ownership_voucher_store_driver: 6 | Directory: 7 | path: {{ test_dir }}/ownership_vouchers/ 8 | trusted_device_keys_path: {{ keys_path }}/device_ca_cert.pem 9 | owner_private_key_path: {{ keys_path }}/owner_key.der 10 | owner_public_key_path: {{ keys_path }}/owner_cert.pem 11 | owner_addresses: 12 | - transport: HTTP 13 | port: 8079 14 | addresses: 15 | - dns_name: localhost 16 | - transport: HTTP 17 | port: {{ owner_port }} 18 | addresses: 19 | - dns_name: localhost 20 | report_to_rendezvous_endpoint_enabled: true 21 | bind: {{ bind }} 22 | service_info_api_url: "http://localhost:{{ serviceinfo_api_server_port }}/device_info" 23 | service_info_api_authentication: 24 | BearerToken: 25 | token: TestAuthToken 26 | -------------------------------------------------------------------------------- /integration-tests/templates/rendezvous-info.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | - dns: localhost 3 | device_port: 8082 4 | owner_port: 8082 5 | protocol: http 6 | - dns: localhost 7 | delay: 30 8 | device_port: {{ rendezvous_port }} 9 | owner_port: {{ rendezvous_port }} 10 | protocol: http 11 | -------------------------------------------------------------------------------- /integration-tests/templates/rendezvous-server.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | storage_driver: 3 | Directory: 4 | path: {{ config_dir }}/rendezvous_registered/ 5 | session_store_driver: 6 | Directory: 7 | path: {{ config_dir }}/sessions/ 8 | trusted_manufacturer_keys_path: {{ keys_path }}/manufacturer_cert.pem 9 | bind: {{ bind }} 10 | -------------------------------------------------------------------------------- /integration-tests/templates/serviceinfo-api-server.yml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | bind: {{ bind }} 3 | device_specific_store_driver: 4 | Directory: 5 | path: {{ config_dir }}/device_specific_serviceinfo 6 | service_info_auth_token: TestAuthToken 7 | admin_auth_token: TestAdminToken 8 | service_info: 9 | initial_user: 10 | username: {{ user }} 11 | password: {{ password }} 12 | sshkeys: 13 | - {{ sshkey1 }} 14 | - {{ sshkey2 }} 15 | files: 16 | - path: /etc/hosts 17 | permissions: 644 18 | source_path: /etc/hosts 19 | - path: /etc/resolv.conf 20 | source_path: /etc/resolv.conf 21 | commands: 22 | - command: ls 23 | args: 24 | - /etc/hosts 25 | return_stdout: true 26 | return_stderr: true 27 | - command: ls 28 | args: 29 | - /etc/doesnotexist/whatever.foo 30 | may_fail: true 31 | return_stdout: true 32 | return_stderr: true 33 | - command: touch 34 | args: 35 | - {{ keys_path }}/command-testfile 36 | {% if encrypted_disk_label %} 37 | diskencryption_clevis: 38 | - disk_label: {{ encrypted_disk_label }} 39 | binding: 40 | pin: test 41 | config: "{}" 42 | reencrypt: true 43 | {% endif %} 44 | -------------------------------------------------------------------------------- /integration-tests/tests/voucher_tests.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | use std::path::Path; 4 | 5 | use anyhow::{Context, Result}; 6 | 7 | use fdo_data_formats::{ 8 | ownershipvoucher::OwnershipVoucher, DeserializableMany, Error, ProtocolVersion, 9 | }; 10 | 11 | fn test_single_voucher(_path: &Path, voucher: &[u8]) -> Result<()> { 12 | let voucher = OwnershipVoucher::from_pem(&voucher).context("Error parsing OV")?; 13 | 14 | println!("Voucher: {:?}", voucher); 15 | 16 | // Try to iterate over the entries. This will validate everything. 17 | let voucheriter = voucher 18 | .iter_entries() 19 | .context("Error constructing voucher iterator")?; 20 | for entry in voucheriter { 21 | println!("Entry: {:?}", entry.context("Error parsing OV Entry")?); 22 | } 23 | 24 | Ok(()) 25 | } 26 | 27 | fn execute_for_each_voucher(directory: &str, init: B, mut f: F) -> Result 28 | where 29 | F: FnMut(B, &std::path::Path, &[u8]) -> Result, 30 | { 31 | let mut result = init; 32 | 33 | for path in std::fs::read_dir(directory).context("Error listing vouchers")? { 34 | let path = path.context("Error getting voucher path")?.path(); 35 | 36 | result = f( 37 | result, 38 | &path, 39 | &std::fs::read(&path).context("Error reading voucher")?, 40 | )?; 41 | } 42 | 43 | Ok(result) 44 | } 45 | 46 | #[test] 47 | fn test_vouchers() -> Result<()> { 48 | let success = execute_for_each_voucher("vouchers/v101/", true, |success, path, voucher| { 49 | Ok(success && test_single_voucher(&path, voucher).is_ok()) 50 | }) 51 | .context("Error running tests")?; 52 | 53 | if success { 54 | Ok(()) 55 | } else { 56 | Err(anyhow::anyhow!("One or more vouchers failed")) 57 | } 58 | } 59 | 60 | #[test] 61 | fn test_multiple_vouchers_raw() -> Result<()> { 62 | let mut count = 0; 63 | 64 | let buffer = 65 | execute_for_each_voucher("vouchers/v101/", Vec::new(), |mut buffer, _, voucher| { 66 | count += 1; 67 | 68 | let pemblock = pem::parse(&voucher).context("Error parsing OV PEM")?; 69 | buffer.extend_from_slice(pemblock.contents()); 70 | 71 | Ok(buffer) 72 | })?; 73 | 74 | let parsed = OwnershipVoucher::deserialize_many_from_reader(&*buffer) 75 | .context("Error parsing multiple vouchers")?; 76 | 77 | assert_eq!(count, parsed.len()); 78 | 79 | Ok(()) 80 | } 81 | 82 | #[test] 83 | fn test_multiple_vouchers_pem() -> Result<()> { 84 | let mut count = 0; 85 | 86 | let buffer = 87 | execute_for_each_voucher("vouchers/v101/", Vec::new(), |mut buffer, _, voucher| { 88 | count += 1; 89 | let pemblock = pem::parse(&voucher).context("Error parsing OV PEM")?; 90 | let pemstring = pem::encode(&pemblock); 91 | buffer.extend_from_slice(&pemstring.as_bytes()); 92 | Ok(buffer) 93 | })?; 94 | 95 | let parsed = 96 | OwnershipVoucher::many_from_pem(&*buffer).context("Error parsing multiple vouchers")?; 97 | 98 | assert_eq!(count, parsed.len()); 99 | 100 | Ok(()) 101 | } 102 | 103 | // We explicitly decided to drop support for the old format. 104 | #[test] 105 | fn test_voucher_v100() -> Result<()> { 106 | let success = execute_for_each_voucher("vouchers/v100/", true, |success, _path, voucher| { 107 | let voucher_result = OwnershipVoucher::from_pem(&voucher); 108 | Ok(success 109 | && matches!( 110 | voucher_result, 111 | Err(Error::UnsupportedVersion(Some(ProtocolVersion::Version1_0))) 112 | )) 113 | }) 114 | .context("Error running tests")?; 115 | 116 | if success { 117 | Ok(()) 118 | } else { 119 | Err(anyhow::anyhow!("One or more vouchers failed")) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /integration-tests/util_bins/clevis-decrypt-test: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: 3 | # 4 | # Copyright (c) 2017 Red Hat, Inc. 5 | # Author: Nathaniel McCallum 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | [ $# -eq 1 ] && [ "$1" == "--summary" ] && exit 2 22 | 23 | read -r -d . hdr 24 | 25 | if [ "$(jose fmt -q "$hdr" -SyOg clevis -g pin -u-)" != "test" ]; then 26 | echo "JWE pin mismatch!" >&2 27 | exit 1 28 | fi 29 | 30 | jwk="$(jose fmt -q "$hdr" -SyOg clevis -g test -g jwk -Oo-)" || exit 1 31 | 32 | exec jose jwe dec -k- -i- < <(echo -n "$jwk$hdr."; /bin/cat) 33 | -------------------------------------------------------------------------------- /integration-tests/util_bins/clevis-encrypt-test: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: 3 | # 4 | # Copyright (c) 2017 Red Hat, Inc. 5 | # Author: Nathaniel McCallum 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | [ $# -eq 1 ] && [ "$1" == "--summary" ] && exit 2 22 | 23 | if ! cfg="$(jose fmt -j "$1" -Oo- 2>/dev/null)"; then 24 | echo "Configuration is malformed!" >&2 25 | exit 1 26 | fi 27 | 28 | jwk="$(jose jwk gen -i '{"alg":"A256GCM"}')" 29 | jwe='{"protected":{"clevis":{"pin":"test","test":{}}}}' 30 | 31 | if ! jose fmt -j "$cfg" -g fail -T; then 32 | jwe="$(jose fmt -j "$jwe" -Og protected -g clevis -g test -j "$jwk" -Os jwk -UUUUo-)" 33 | fi 34 | 35 | exec jose jwe enc -i- -k- -I- -c < <(echo -n "$jwe$jwk"; /bin/cat) 36 | -------------------------------------------------------------------------------- /integration-tests/vouchers/v100/voucher1: -------------------------------------------------------------------------------- 1 | -----BEGIN OWNERSHIP VOUCHER----- 2 | hIYYZFBW2CQ38BhGIYaFYcSkgyTCgYWCBXgfZmRvMTAud2VzdHVzLmNsb3VkYXBw 3 | LmF6dXJlLmNvbYIDGFCCDAGCAkSKW8NVggQYUGtKYXZhIERldmljZYMmAVhbMFkw 4 | EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELAJwkDKz/BaWq1Wx7PjkR5W5LLIbamgS 5 | ZeVNUlyFM/t0sMAxAWbvEbDzKu924TX4as3WVjMmfekysx30PlDGJYIvWCCgzrFh 6 | GnoR/RXIaWXhLTxiaqx9O8kWeWLiJ4ZPYQTrSIIFWCDirRmAbMYPvxHsG3Uqjioo 7 | IqbAifJKPBVzTBtIwc7psoJZAQEwgf4wgaWgAwIBAgIGAX0IDrzzMAoGCCqGSM49 8 | BAMCMA0xCzAJBgNVBAMMAkNBMB4XDTIxMTExMDA0MTUyM1oXDTMxMTEwODA0MTUy 9 | M1owADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKWC8HLsakdG2OfJdFWKbE7G 10 | lM6RQgqXjd25ldIB6ecSxzMLwRUcjrZWMTdF2sfHBA7H7yLlSWIWMrWzhj5GfJgw 11 | CgYIKoZIzj0EAwIDSAAwRQIgE3Ndd4XyO02Yd585GQBDn0IMiOmY+Uoy8rXAUyQm 12 | GvQCIQC0I5FYMjRGxEtV/YbrBX5InwV0PqyLH+FAYP4k4ioW11kBJjCCASIwgcmg 13 | AwIBAgIJAKTTA66YD1PxMAoGCCqGSM49BAMCMA0xCzAJBgNVBAMMAkNBMCAXDTE5 14 | MDQyNDE0NDY0N1oYDzIwNTQwNDE1MTQ0NjQ3WjANMQswCQYDVQQDDAJDQTBZMBMG 15 | ByqGSM49AgEGCCqGSM49AwEHA0IABCwCcJAys/wWlqtVsez45EeVuSyyG2poEmXl 16 | TVJchTP7dLDAMQFm7xGw8yrvduE1+GrN1lYzJn3pMrMd9D5QxiWjEDAOMAwGA1Ud 17 | EwQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIhAKVBm4I2E9JOtwHkQLTzNovlZ1un 18 | JGGicrxS7rlsPkFAAiBOcNJ7Yxy278JqoMAn4eU+rvHsUHQgNoPR7Lud4SnGkoHS 19 | hEOhASagWKmDgi9YICgBC64NGPDRFV4nqf8yjb31Fg8N9A0MyzExVEAgNsHEgi9Y 20 | IPq6wNBjf1QNPuiSYY1vVm9MNbJRAdFjYB5/6NQaRhDJgyYBWFswWTATBgcqhkjO 21 | PQIBBggqhkjOPQMBBwNCAARZVQTYbQYvLyxyYA7JDKFwGIX99JR3eL86DtcNKGIl 22 | vYixsJlJGq3V6TXkht4IjnPsEd5rYZkaBorrdzIPXmA0WEDQkSFDrXCzg5tSQSkH 23 | ZZ7EoI+BprDklNNiBwy3PRUUYjPFhrK+p9baYbq7XcKNY8tw3G5CbXpaG0RCy37b 24 | 2nvM 25 | -----END OWNERSHIP VOUCHER----- 26 | -----BEGIN EC PRIVATE KEY----- 27 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgeDjm+Fcf2bZJ3M3F 28 | Tnlyn3jJv3rYmg2fwX1ml7rNM2qhRANCAARZVQTYbQYvLyxyYA7JDKFwGIX99JR3 29 | eL86DtcNKGIlvYixsJlJGq3V6TXkht4IjnPsEd5rYZkaBorrdzIPXmA0 30 | -----END EC PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /integration-tests/vouchers/v100/voucher2: -------------------------------------------------------------------------------- 1 | -----BEGIN OWNERSHIP VOUCHER----- 2 | hIYYZFAcB1+DaJNFuatF//J2+8KegYWCBXdmZG8tdGVzdC5wdWl0ZXJ3aWprLm9y 3 | Z4IDGR+QggwBggJEdQAAAYIEGR+Qa0phdmEgRGV2aWNlgyYBWFswWTATBgcqhkjO 4 | PQIBBggqhkjOPQMBBwNCAAQsAnCQMrP8FparVbHs+ORHlbksshtqaBJl5U1SXIUz 5 | +3SwwDEBZu8RsPMq73bhNfhqzdZWMyZ96TKzHfQ+UMYlgi9YIMsw7q/oJzq4yOFv 6 | 3IWvtLeDwDKI9g6wxNROhZv4udakggVYIBGjgG8mRBE601elD3OpvfnVWhUr5UDY 7 | eSsIEAz/WC/xglkBADCB/TCBpaADAgECAgYBfQf+DtUwCgYIKoZIzj0EAwIwDTEL 8 | MAkGA1UEAwwCQ0EwHhcNMjExMTEwMDM1NzEwWhcNMzExMTA4MDM1NzEwWjAAMFkw 9 | EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpYLwcuxqR0bY58l0VYpsTsaUzpFCCpeN 10 | 3bmV0gHp5xLHMwvBFRyOtlYxN0Xax8cEDsfvIuVJYhYytbOGPkZ8mDAKBggqhkjO 11 | PQQDAgNHADBEAiA4QaoUTfGUC6TXYH+2ErwKFkFxv86vLZSaKy/eXYoPMwIgBM7v 12 | 0TItnYTXJbfxPrTfQnS7LO7eSqDeMUQi+N3I/utZASYwggEiMIHJoAMCAQICCQCk 13 | 0wOumA9T8TAKBggqhkjOPQQDAjANMQswCQYDVQQDDAJDQTAgFw0xOTA0MjQxNDQ2 14 | NDdaGA8yMDU0MDQxNTE0NDY0N1owDTELMAkGA1UEAwwCQ0EwWTATBgcqhkjOPQIB 15 | BggqhkjOPQMBBwNCAAQsAnCQMrP8FparVbHs+ORHlbksshtqaBJl5U1SXIUz+3Sw 16 | wDEBZu8RsPMq73bhNfhqzdZWMyZ96TKzHfQ+UMYloxAwDjAMBgNVHRMEBTADAQH/ 17 | MAoGCCqGSM49BAMCA0gAMEUCIQClQZuCNhPSTrcB5EC08zaL5WdbpyRhonK8Uu65 18 | bD5BQAIgTnDSe2Mctu/CaqDAJ+HlPq7x7FB0IDaD0ey7neEpxpKB0oRDoQEmoFip 19 | g4IvWCBQMTcbf7iZFesJhCFnaFtLBJj/JsI1o1psuDS5ZxCunoIvWCDXilmNPrGq 20 | 2jJb+FiORpMK9oJ1ZWnKXb/FRh/YrhT3sIMmAVhbMFkwEwYHKoZIzj0CAQYIKoZI 21 | zj0DAQcDQgAEWVUE2G0GLy8scmAOyQyhcBiF/fSUd3i/Og7XDShiJb2IsbCZSRqt 22 | 1ek15IbeCI5z7BHea2GZGgaK63cyD15gNFhANquKo+OlW8eTZFnA6xFlRvmKFUMw 23 | qZk4sC9+AZi4OTGZbPaioHIb7r0qFYtbugU8SdNK4HiFCoNvvckxZzwx1Q== 24 | -----END OWNERSHIP VOUCHER----- 25 | -----BEGIN EC PRIVATE KEY----- 26 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgeDjm+Fcf2bZJ3M3F 27 | Tnlyn3jJv3rYmg2fwX1ml7rNM2qhRANCAARZVQTYbQYvLyxyYA7JDKFwGIX99JR3 28 | eL86DtcNKGIlvYixsJlJGq3V6TXkht4IjnPsEd5rYZkaBorrdzIPXmA0 29 | -----END EC PRIVATE KEY----- 30 | -------------------------------------------------------------------------------- /integration-tests/vouchers/v101/voucher1: -------------------------------------------------------------------------------- 1 | -----BEGIN OWNERSHIP VOUCHER----- 2 | hRhlWOmGGGVQGJByeaQdBJquPE2kzmHBS4GEggVYIXgfZmRvMTAud2VzdHVzLmNs 3 | b3VkYXBwLmF6dXJlLmNvbYIDQhhQggRCGFCCDEEBanRlc3RkZXZpY2WDCgFYWzBZ 4 | MBMGByqGSM49AgEGCCqGSM49AwEHA0IABMulxypvqpxXlsylJb2gzzRm0sTkOyNw 5 | m1vU4dvwu7vp1orKYAsGESpbHVDZAlfFUblW7VXTErka9dIR/Y2o70CCOCpYMLMa 6 | m9xVqignf0m2b/SUo4FNun5bRFk3jtNKU+0WOcrh22uLWV67NKy8yqQkt8KrUIIG 7 | WDD+C1KJjSlUm9pwq6R46vrVTEQjZSzWe+8SXNRDfKnCBPf+UujY0pZV7AVJNiNj 8 | vviCWQFCMIIBPjCB5qADAgECAgkAgqhz5c06HcIwCgYIKoZIzj0EAwMwNjELMAkG 9 | A1UEBhMCVVMxFjAUBgNVBAoMDVJIRUwgZm9yIEVkZ2UxDzANBgNVBAMMBkRldmlj 10 | ZTAeFw0yMjAxMjcxODE1MjdaFw0zMjAxMjUxODE1MjdaMBUxEzARBgNVBAMMCnRl 11 | c3RkZXZpY2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQpV6PcWvdgiA/7xYyb 12 | uSrA41BZ4b8D0wWE1Gj+MrwHOOU56SdsRt+CpxZrNApF9s7H+K3X/SMBGGJlJISo 13 | 9vBqMAoGCCqGSM49BAMDA0cAMEQCIHYsTyc/c4+sLAR+eomdqRppdmDlwPc7Kq2I 14 | qI4Y1w01AiBL30GGyP3zSEUlFa5pxDAduvV71peKx4ml7+iXJmKB+VkBxTCCAcEw 15 | ggFnoAMCAQICFBCCYDeOvTX9qpd6Zog0ZLOn1RvNMAoGCCqGSM49BAMCMDYxCzAJ 16 | BgNVBAYTAlVTMRYwFAYDVQQKDA1SSEVMIGZvciBFZGdlMQ8wDQYDVQQDDAZEZXZp 17 | Y2UwHhcNMjExMTA4MTUxNTQyWhcNMjIxMTA4MTUxNTQyWjA2MQswCQYDVQQGEwJV 18 | UzEWMBQGA1UECgwNUkhFTCBmb3IgRWRnZTEPMA0GA1UEAwwGRGV2aWNlMFkwEwYH 19 | KoZIzj0CAQYIKoZIzj0DAQcDQgAEdf4TxWqrt0lgh69K/J5Tz5CNf54knr3NpxD3 20 | 5W4PNKDWFBlwZpnHNwkhvPuOH38hXHQ03nE+cQLubfAYh1nNQaNTMFEwHQYDVR0O 21 | BBYEFJBIUreAz0iPNQ1UGZedxEN0fA5QMB8GA1UdIwQYMBaAFJBIUreAz0iPNQ1U 22 | GZedxEN0fA5QMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIgOkji 23 | XMNfbXxFFGXS+ckJtWBrElfDOvoBzT1okl/SMvYCIQDQC6sCT6Pje/CYUvgwBJuL 24 | U6oLlIhwSzOd+0CC/XfF9IHShEOhASagWMyEgjgqWDCCQpavl8MV4Co8Xb0t8NJ+ 25 | O+iOvNBjSSIL3nPXKCikLEAQ1TluR5IXbRnP6ZvVM4OCOCpYMJ2ayEc7SuXRf0rT 26 | 557xAhQC02d8lgxeyDGPZCeSx+5wR1bzVbyaIRnzkl78m/EGefaDCgFYWzBZMBMG 27 | ByqGSM49AgEGCCqGSM49AwEHA0IABKgmbzfMhVB6HP8o8shKFqadSGmbdN/jxUSe 28 | X0A52zppEj1r931f2j7QVloFmrNEVFls1tXImq6yAoajQ/c73ztYQD4Q5FwsT/E0 29 | Y8KUOzsAzWy8qDao5PdBMSqy/eMjaDdFXOj97qB6n9HbDf7i9xvJc6fo3m08U2AA 30 | 3c9IM0kkLss= 31 | -----END OWNERSHIP VOUCHER----- 32 | -------------------------------------------------------------------------------- /integration-tests/vouchers/v101/voucher2: -------------------------------------------------------------------------------- 1 | -----BEGIN OWNERSHIP VOUCHER----- 2 | hRhlWNmGGGVQrADaYQfoQF6sxJSu8/aJZoGFggVYGHdmZG8tdGVzdC5wdWl0ZXJ3 3 | aWprLm9yZ4IDQxkfkIIMQQGCAkVEA35nBIIEQxkfkGpEZW1vRGV2aWNlgwoBWFsw 4 | WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASFAojJI3GIYx0fz5mx9QO53U59MwY2 5 | QvptBwTTyvh1tikpT/CA0cf/N6JtSJBVj1/JHY62ccI+Jzv1sqxffc+Mgi9YIMkD 6 | 7w1d9Rditn2rm+psJL1ryFUO/j3hAo/X4ePOmMmaggVYIPRzo3IoKFbYyLH3qA/2 7 | hsRopg1P/uTeTphdxo4OvTryglkBITCCAR0wgcSgAwIBAgIIOlx4CVjdSPcwCgYI 8 | KoZIzj0EAwIwFDESMBAGA1UEAwwJRmRvRW50aXR5MCAXDTIyMDEyNjE4NDEyMloY 9 | DzIwNTEwODIyMTg0MTIyWjAUMRIwEAYDVQQDDAlGZG9FbnRpdHkwWTATBgcqhkjO 10 | PQIBBggqhkjOPQMBBwNCAAT9kNe8A3UoGtGfqZPxFQyzqcfor0l8yS3xDd/H/gJ7 11 | ml5RRmLMpI4xCbC3P4C6Sq+S5Qz/dx76VZfHTIWDscsrMAoGCCqGSM49BAMCA0gA 12 | MEUCIQCIIoQnT5Asro8EmzfcBr5FrjOmcOgN0Yu365/cW1Dy4QIgZ9RBfN8K1QSa 13 | k1oMjJ5FU8hw9UQPwUWh30sq5QXKTnJZASEwggEdMIHEoAMCAQICCDlmInW5jU2q 14 | MAoGCCqGSM49BAMCMBQxEjAQBgNVBAMMCUZkb0VudGl0eTAgFw0yMjAxMjUyMTA4 15 | NTVaGA8yMDUxMDgyMTIxMDg1NVowFDESMBAGA1UEAwwJRmRvRW50aXR5MFkwEwYH 16 | KoZIzj0CAQYIKoZIzj0DAQcDQgAEhQKIySNxiGMdH8+ZsfUDud1OfTMGNkL6bQcE 17 | 08r4dbYpKU/wgNHH/zeibUiQVY9fyR2OtnHCPic79bKsX33PjDAKBggqhkjOPQQD 18 | AgNIADBFAiBz+VC2aM71z1MXQFRcz/sHvEwgVDP/t7xLX4oyQ9ZEsAIhAPx7WEF5 19 | FtUnS75o+xWbHhTgExaGtb/MSg4ThMHKzcDbgtKEQ6EBJqBYqoSCL1ggwtpzFQH0 20 | kcSryES1JJSw5h14Pw7ySuGuJvHvQLCsmAGCL1ggGl7Gt04AvazHNeLTobxjNDoX 21 | fEQv+5MNnh3m+oPsCEn2gwoBWFswWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATP 22 | fZrcEcFtC+WhRQKupnWs6UJZN0DJdsAZz2y0QUFQjDxKwxg8liPdykKXv6HRYk3a 23 | EWB0Y5LLYLi2TwSUjAuqWECYEXSqik9ojHPlfX+HJ1s3ANJyCCSsO3Q48AavI8IE 24 | m4EnyxFjo2oHu6BwaM2BHJbwX3Om25pnYBcnaULlebro0oRDoQEmoFiqhIIvWCBD 25 | qrqxGw1yAohEIPfrGSes7zd1Kjd/nfQxLwQZcm0APYIvWCAaXsa3TgC9rMc14tOh 26 | vGM0Ohd8RC/7kw2eHeb6g+wISfaDCgFYWzBZMBMGByqGSM49AgEGCCqGSM49AwEH 27 | A0IABKTNIdBDP3pgAZD/WrgP93jjWCbZLn0khPwrVMXK1xtO5ebpBnL678eB6hnR 28 | 45MwexLc6wfPZly3ZI3zSDnPzZtYQIXI2dpaDa5EvdvydJmbxgoSz6KMKWcttjfL 29 | acSlIyfk2RRaM+Y9a9v0HeIfjgleMXXgKFbe6j8XyY9FfLHcpUc= 30 | -----END OWNERSHIP VOUCHER----- 31 | -------------------------------------------------------------------------------- /integration-tests/vouchers/v101/voucher3: -------------------------------------------------------------------------------- 1 | -----BEGIN OWNERSHIP VOUCHER----- 2 | hRhlWMuGGGVQicsX/ZXnTeijamhpJqf4j4GDggVYGHdmZG8tdGVzdC5wdWl0ZXJ3 3 | aWprLm9yZ4IDQxkfkIIMQQFqRGVtb0RldmljZYMKAVhbMFkwEwYHKoZIzj0CAQYI 4 | KoZIzj0DAQcDQgAEhQKIySNxiGMdH8+ZsfUDud1OfTMGNkL6bQcE08r4dbYpKU/w 5 | gNHH/zeibUiQVY9fyR2OtnHCPic79bKsX33PjIIvWCAHJdVmK70OE0caGSn+HRSx 6 | Xx8SPoFyY+IfRZ/fEvyZ/oIFWCAN/dJ7KHZkt1QqHV8M7JraCzlinV72MNV0LJHZ 7 | qMU1WIJZASEwggEdMIHEoAMCAQICCHglWINPRUxsMAoGCCqGSM49BAMCMBQxEjAQ 8 | BgNVBAMMCUZkb0VudGl0eTAgFw0yMjAxMjYxODI1MzRaGA8yMDUxMDgyMjE4MjUz 9 | NFowFDESMBAGA1UEAwwJRmRvRW50aXR5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD 10 | QgAE/ZDXvAN1KBrRn6mT8RUMs6nH6K9JfMkt8Q3fx/4Ce5peUUZizKSOMQmwtz+A 11 | ukqvkuUM/3ce+lWXx0yFg7HLKzAKBggqhkjOPQQDAgNIADBFAiEApcClb9EoZE5i 12 | ydyPjtoZVQzfIsvL2ARYP9hLN2k27EYCIGPaVmXGQ7MZyuG1LX6PgCM69IgLuOJZ 13 | CCPNYWov9wXoWQEhMIIBHTCBxKADAgECAgg5ZiJ1uY1NqjAKBggqhkjOPQQDAjAU 14 | MRIwEAYDVQQDDAlGZG9FbnRpdHkwIBcNMjIwMTI1MjEwODU1WhgPMjA1MTA4MjEy 15 | MTA4NTVaMBQxEjAQBgNVBAMMCUZkb0VudGl0eTBZMBMGByqGSM49AgEGCCqGSM49 16 | AwEHA0IABIUCiMkjcYhjHR/PmbH1A7ndTn0zBjZC+m0HBNPK+HW2KSlP8IDRx/83 17 | om1IkFWPX8kdjrZxwj4nO/WyrF99z4wwCgYIKoZIzj0EAwIDSAAwRQIgc/lQtmjO 18 | 9c9TF0BUXM/7B7xMIFQz/7e8S1+KMkPWRLACIQD8e1hBeRbVJ0u+aPsVmx4U4BMW 19 | hrW/zEoOE4TBys3A24HShEOhASagWKqEgi9YIMEkQmiueiUhYKP+pm1mC4E1MuVX 20 | w2Ofph5T/3gbJtfpgi9YIB5Ru4JH/MhoTkw9L5gONhIfV21nBUSVvjWKs4/WAM/s 21 | 9oMKAVhbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz32a3BHBbQvloUUCrqZ1 22 | rOlCWTdAyXbAGc9stEFBUIw8SsMYPJYj3cpCl7+h0WJN2hFgdGOSy2C4tk8ElIwL 23 | qlhAHQ4iQeTx0HTPKzoW/Cr2AqPP+WnrnCDYW5XwFdd68Y+3sKI328sV6ZXBiFAw 24 | uT/3c8sLWHnCqmtcOp3a8NX4Jg== 25 | -----END OWNERSHIP VOUCHER----- 26 | -----BEGIN EC PRIVATE KEY----- 27 | MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAVKbexx9Dmt1cPfJpi 28 | 6lDdu/k10hHp1N9cdUB57LDtzQ== 29 | -----END EC PRIVATE KEY----- 30 | -------------------------------------------------------------------------------- /konflux/README.md: -------------------------------------------------------------------------------- 1 | # Konflux integration test cases 2 | As RedHat uses Konflux to build, verify and release containers now, all fdo containers will be built in konflux. The integration test cases defined in this folder will be triggered by konflux when new fdo container images are built. 3 | 4 | ## How konflux trigger test cases 5 | konflux defines integration test workflow in this file: (when writing this doc, below files are not merged into konflux yet, will update it later) 6 | - https://gitlab.cee.redhat.com/releng/konflux-release-data/-/merge_requests/2167/diffs#3904e36f53d22f073f197dedd0bd6355289a0053 7 | 8 | Within this file, it defines fdo repo url and test file path: 9 | - fdo repo: https://gitlab.cee.redhat.com/releng/konflux-release-data/-/merge_requests/2167/diffs#3904e36f53d22f073f197dedd0bd6355289a0053_0_15 10 | - test file: https://gitlab.cee.redhat.com/releng/konflux-release-data/-/merge_requests/2167/diffs#3904e36f53d22f073f197dedd0bd6355289a0053_0_19 11 | 12 | When konflux trigger integration test workflow, it will go to fdo repo and get the test file, and execute the tasks defined in test file. 13 | 14 | ## Test files in this folder 15 | There are four pipeline files and one test file in this folder. 16 | - fdo-manufacturing-server-integration-tests.yaml 17 | - fdo-owner-onboarding-server-integration-tests.yaml 18 | - fdo-rendezvous-server-integration-tests.yaml 19 | - fdo-serviceinfo-api-server-integration-tests.yaml 20 | - fdo-container-test.yaml 21 | 22 | ## Test case definition (manufacturing server test case as example) 23 | There are two yaml files for manufacturing server test. 24 | 25 | - fdo-manufacturing-server-integration-tests.yaml: This is the test file defined in konflux integration test workflow file, this file defines parameters, env variables and tasks. It also defines a task file path and pass all the parameters to that task file. 26 | Example of parameters: 27 | - SNAPSHOT: a parameter from konflux, it is basically the description of container that was built. 28 | - GIT_URL: tell konflux which git repo to get 29 | - GIT_REF: tell konflux which branch to use 30 | 31 | - fdo-container-test.yaml: This is the task file that defined in fdo-manufacturing-server-integration-tests.yaml, it defines some actions: 32 | - get secrets from konflux 33 | - set parameters 34 | - get the url of fdo container we want to test 35 | - run bash script 36 | 37 | ## How to get fdo container 38 | In task yaml file, we can use parameter SNAPSHOT to retrieve container image url, the returned value will be like quay.io/fido-fdo/serviceinfo-api-server 39 | - IMAGE=$(echo "${SNAPSHOT}" | jq -r ".components[]|select(.name==\"$COMPONENT\")|.containerImage") 40 | 41 | ## How to set test runner 42 | In task yaml file, we can specify the os and version of test runner to execute all the actions. 43 | Set image like this, "- image: quay.io/testing-farm/cli:latest" 44 | - It means to use the latest testing-farm cli container as runner, the benefit is that if you want to reserve a testing-farm machine to run test cases, you can use testing-farm command directly in script section and no need to install testing-farm related packages. 45 | - If you want to run test cases in this runner directly, just write all test cases in script section. 46 | 47 | Depends on the os of test runner, you need to use different commands in bash script. 48 | - For testing-farm runner, run apk to install packages 49 | - For RHEL, Centos-Stream, Fedora, run dnf or yum to install packages. 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /konflux/fdo-container-test.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Task 3 | metadata: 4 | name: fdo-container-test 5 | spec: 6 | description: Initiate test given a list of container images 7 | params: 8 | - name: SNAPSHOT 9 | description: A list of container images that should undergo testing 10 | - name: GIT_URL 11 | description: URL of the GIT repository that contains the tests. 12 | - name: GIT_REF 13 | description: Branch of the git repository used containing the tests 14 | volumes: 15 | - name: podinfo 16 | downwardAPI: 17 | items: 18 | - path: "labels" 19 | fieldRef: 20 | fieldPath: metadata.labels 21 | - path: "annotations" 22 | fieldRef: 23 | fieldPath: metadata.annotations 24 | steps: 25 | - image: quay.io/testing-farm/cli:latest 26 | volumeMounts: 27 | - name: podinfo 28 | mountPath: /etc/podinfo 29 | env: 30 | - name: SNAPSHOT 31 | value: $(params.SNAPSHOT) 32 | - name: GIT_URL 33 | value: $(params.GIT_URL) 34 | - name: GIT_REF 35 | value: $(params.GIT_REF) 36 | script: | 37 | #!/usr/bin/env bash 38 | sed -i -e 's/v3\.15/v3\.16/g' /etc/apk/repositories 39 | apk update 40 | apk add --upgrade apk-tools 41 | apk upgrade --available 42 | apk add skopeo jq grep curl 43 | 44 | cat /etc/podinfo/labels 45 | PR_NAME=$(cat /etc/podinfo/labels | grep -oP '(?<=pipelineRun=")[^"]+') 46 | COMPONENT=$(cat /etc/podinfo/labels | grep -oP '(?<=component=")[^"]+') 47 | echo "PR_NAME:$PR_NAME" 48 | echo "COMPONENT:$COMPONENT" 49 | 50 | echo "${SNAPSHOT}" 51 | IMAGE=$(echo "${SNAPSHOT}" | jq -r ".components[]|select(.name==\"$COMPONENT\")|.containerImage") 52 | COUNT=$(echo "${SNAPSHOT}" | jq -r ".components|map(select(.name==\"$COMPONENT\"))|length") 53 | if [[ ${COUNT} -ne 1 ]]; then 54 | echo "Error: multiple images: ${IMAGES} in this application with component name: ${COMPONENT}" 55 | exit 1 56 | fi 57 | echo $IMAGE 58 | 59 | IMAGE_NAME=$(echo "${IMAGE##*/}" | cut -d @ -f 1) 60 | IMAGE_TAG=$(echo "${IMAGE##*/}" | cut -d : -f 2) 61 | echo "IMAGE_NAME:$IMAGE_NAME" 62 | echo "IMAGE_TAG:$IMAGE_TAG" 63 | 64 | skopeo inspect docker://"$IMAGE" > skopeo_inspect.json 65 | cat skopeo_inspect.json 66 | NAME=$(cat skopeo_inspect.json | jq -r ".Name") 67 | echo "NAME:$NAME" 68 | 69 | if [[ "$NAME" =~ "$IMAGE_NAME" ]]; then 70 | exit 0 71 | else 72 | exit 1 73 | fi 74 | timeout: "2h" 75 | -------------------------------------------------------------------------------- /konflux/fdo-manufacturing-server-integration-tests.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Pipeline 3 | metadata: 4 | name: fdo manufacturing server integration tests 5 | spec: 6 | description: >- 7 | Expects a list of container images to be provided via the SNAPSHOT parameter. 8 | params: 9 | - name: SNAPSHOT 10 | description: A list of fdo container images that should undergo testing 11 | type: string 12 | - name: GIT_URL 13 | description: URL of the GIT repository that contains the tests. 14 | default: "https://github.com/fdo-rs/fido-device-onboard-rs" 15 | type: string 16 | - name: GIT_REF 17 | default: "main" 18 | description: Branch of the git repository used containing the tests 19 | type: string 20 | tasks: 21 | - name: fdo-container-test 22 | taskRef: 23 | resolver: git 24 | params: 25 | - name: url 26 | value: $(params.GIT_URL) 27 | - name: revision 28 | value: $(params.GIT_REF) 29 | - name: pathInRepo 30 | value: konflux/fdo-container-test.yaml 31 | params: 32 | - name: SNAPSHOT 33 | value: $(params.SNAPSHOT) 34 | - name: GIT_URL 35 | value: $(params.GIT_URL) 36 | - name: GIT_REF 37 | value: $(params.GIT_REF) 38 | timeout: "2h" 39 | -------------------------------------------------------------------------------- /konflux/fdo-owner-onboarding-server-integration-tests.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Pipeline 3 | metadata: 4 | name: fdo owner onboarding server integration tests 5 | spec: 6 | description: >- 7 | Expects a list of container images to be provided via the SNAPSHOT parameter. 8 | params: 9 | - name: SNAPSHOT 10 | description: A list of fdo container images that should undergo testing 11 | type: string 12 | - name: GIT_URL 13 | description: URL of the GIT repository that contains the tests. 14 | default: "https://github.com/fdo-rs/fido-device-onboard-rs" 15 | type: string 16 | - name: GIT_REF 17 | default: "main" 18 | description: Branch of the git repository used containing the tests 19 | type: string 20 | tasks: 21 | - name: fdo-container-test 22 | taskRef: 23 | resolver: git 24 | params: 25 | - name: url 26 | value: $(params.GIT_URL) 27 | - name: revision 28 | value: $(params.GIT_REF) 29 | - name: pathInRepo 30 | value: konflux/fdo-container-test.yaml 31 | params: 32 | - name: SNAPSHOT 33 | value: $(params.SNAPSHOT) 34 | - name: GIT_URL 35 | value: $(params.GIT_URL) 36 | - name: GIT_REF 37 | value: $(params.GIT_REF) 38 | timeout: "2h" 39 | -------------------------------------------------------------------------------- /konflux/fdo-rendezvous-server-integration-tests.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Pipeline 3 | metadata: 4 | name: fdo rendezvous server integration tests 5 | spec: 6 | description: >- 7 | Expects a list of container images to be provided via the SNAPSHOT parameter. 8 | params: 9 | - name: SNAPSHOT 10 | description: A list of fdo container images that should undergo testing 11 | type: string 12 | - name: GIT_URL 13 | description: URL of the GIT repository that contains the tests. 14 | default: "https://github.com/fdo-rs/fido-device-onboard-rs" 15 | type: string 16 | - name: GIT_REF 17 | default: "main" 18 | description: Branch of the git repository used containing the tests 19 | type: string 20 | tasks: 21 | - name: fdo-container-test 22 | taskRef: 23 | resolver: git 24 | params: 25 | - name: url 26 | value: $(params.GIT_URL) 27 | - name: revision 28 | value: $(params.GIT_REF) 29 | - name: pathInRepo 30 | value: konflux/fdo-container-test.yaml 31 | params: 32 | - name: SNAPSHOT 33 | value: $(params.SNAPSHOT) 34 | - name: GIT_URL 35 | value: $(params.GIT_URL) 36 | - name: GIT_REF 37 | value: $(params.GIT_REF) 38 | timeout: "2h" 39 | -------------------------------------------------------------------------------- /konflux/fdo-serviceinfo-api-server-integration-tests.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1 2 | kind: Pipeline 3 | metadata: 4 | name: fdo serviceinfo api server integration tests 5 | spec: 6 | description: >- 7 | Expects a list of container images to be provided via the SNAPSHOT parameter. 8 | params: 9 | - name: SNAPSHOT 10 | description: A list of fdo container images that should undergo testing 11 | type: string 12 | - name: GIT_URL 13 | description: URL of the GIT repository that contains the tests. 14 | default: "https://github.com/fdo-rs/fido-device-onboard-rs" 15 | type: string 16 | - name: GIT_REF 17 | default: "main" 18 | description: Branch of the git repository used containing the tests 19 | type: string 20 | tasks: 21 | - name: fdo-container-test 22 | taskRef: 23 | resolver: git 24 | params: 25 | - name: url 26 | value: $(params.GIT_URL) 27 | - name: revision 28 | value: $(params.GIT_REF) 29 | - name: pathInRepo 30 | value: konflux/fdo-container-test.yaml 31 | params: 32 | - name: SNAPSHOT 33 | value: $(params.SNAPSHOT) 34 | - name: GIT_URL 35 | value: $(params.GIT_URL) 36 | - name: GIT_REF 37 | value: $(params.GIT_REF) 38 | timeout: "2h" 39 | -------------------------------------------------------------------------------- /libfdo-data-go/cimport.go: -------------------------------------------------------------------------------- 1 | package libfdo_data 2 | 3 | // #cgo LDFLAGS: -lfdo_data 4 | // #include 5 | import "C" 6 | -------------------------------------------------------------------------------- /libfdo-data-go/errors.go: -------------------------------------------------------------------------------- 1 | package libfdo_data 2 | 3 | // #include 4 | import "C" 5 | import "fmt" 6 | 7 | func getLastError() error { 8 | err := C.fdo_get_last_error() 9 | if err == nil { 10 | return nil 11 | } 12 | 13 | return fmt.Errorf("%s", fromFDOString(err)) 14 | } 15 | -------------------------------------------------------------------------------- /libfdo-data-go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/fedora-iot/fido-device-onboard-rs/libfdo-data-go 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /libfdo-data-go/local.go: -------------------------------------------------------------------------------- 1 | //go:build localbuild 2 | // +build localbuild 3 | 4 | package libfdo_data 5 | 6 | // #cgo LDFLAGS: -lfdo_data -L../target/debug/ 7 | // #cgo CFLAGS: -I../ 8 | import "C" 9 | -------------------------------------------------------------------------------- /libfdo-data-go/ownershipvoucher.go: -------------------------------------------------------------------------------- 1 | package libfdo_data 2 | 3 | // #include 4 | import "C" 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | type OwnershipVoucherList struct { 11 | inner *C.struct_FdoOwnershipVoucherList 12 | } 13 | 14 | func ParseManyOwnershipVouchers(voucherB []byte) (*OwnershipVoucherList, error) { 15 | ctslen := C.size_t(len(voucherB)) 16 | cts := C.CBytes(voucherB) 17 | 18 | voucherlist := C.fdo_ownershipvoucher_many_from_data(cts, ctslen) 19 | if voucherlist == nil { 20 | return nil, fmt.Errorf("failed to parse ownership vouchers: %v", getLastError()) 21 | } 22 | 23 | return &OwnershipVoucherList{ 24 | inner: voucherlist, 25 | }, nil 26 | } 27 | 28 | func (ovl *OwnershipVoucherList) Free() { 29 | C.fdo_ownershipvoucher_list_free(ovl.inner) 30 | } 31 | 32 | func (ovl *OwnershipVoucherList) Len() int { 33 | return int(C.fdo_ownershipvoucher_list_len(ovl.inner)) 34 | } 35 | 36 | func (ovl *OwnershipVoucherList) GetVoucher(index int) (*OwnershipVoucher, error) { 37 | item := C.fdo_ownershipvoucher_list_get(ovl.inner, C.uint64_t(index)) 38 | if item == nil { 39 | return nil, fmt.Errorf("no voucher") 40 | } 41 | 42 | return &OwnershipVoucher{ 43 | inner: item, 44 | should_free: false, 45 | }, nil 46 | } 47 | 48 | type OwnershipVoucher struct { 49 | inner *C.struct_FdoOwnershipVoucher 50 | // Make sure that even if free is called on list items, we don't actually tell 51 | // rust to free the memory (that is owned by the list). 52 | should_free bool 53 | } 54 | 55 | func ParseOwnershipVoucher(voucherB []byte) (*OwnershipVoucher, error) { 56 | ctslen := C.size_t(len(voucherB)) 57 | cts := C.CBytes(voucherB) 58 | 59 | voucher := C.fdo_ownershipvoucher_from_data(cts, ctslen) 60 | if voucher == nil { 61 | return nil, fmt.Errorf("failed to parse: %v", getLastError()) 62 | } 63 | 64 | return &OwnershipVoucher{ 65 | inner: voucher, 66 | should_free: true, 67 | }, nil 68 | } 69 | 70 | func (ov *OwnershipVoucher) Free() { 71 | if ov.should_free { 72 | C.fdo_ownershipvoucher_free(ov.inner) 73 | } 74 | } 75 | 76 | func (ov *OwnershipVoucher) GetProtocolVersion() uint32 { 77 | return uint32(C.fdo_ownershipvoucher_header_get_protocol_version(ov.inner)) 78 | } 79 | 80 | func (ov *OwnershipVoucher) GetGUID() string { 81 | return fromFDOString(C.fdo_ownershipvoucher_header_get_guid(ov.inner)) 82 | } 83 | 84 | func (ov *OwnershipVoucher) GetDeviceInfo() string { 85 | return fromFDOString(C.fdo_ownershipvoucher_header_get_device_info_string(ov.inner)) 86 | } 87 | -------------------------------------------------------------------------------- /libfdo-data-go/strings.go: -------------------------------------------------------------------------------- 1 | package libfdo_data 2 | 3 | // #include 4 | import "C" 5 | 6 | func fromFDOString(c_string *C.char) string { 7 | newval := C.GoString(c_string) 8 | C.fdo_free_string(c_string) 9 | return newval 10 | } 11 | -------------------------------------------------------------------------------- /libfdo-data/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fdo-data" 3 | version = "0.5.5" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | fdo-data-formats = { path = "../data-formats", version = "0.5.5" } 13 | libc = "0.2" 14 | 15 | [build-dependencies] 16 | cbindgen = "0.24.3" 17 | 18 | [dev-dependencies] 19 | tempfile = "3.5.0" 20 | assert-str = "0.1" 21 | serial_test = "2.0.0" 22 | -------------------------------------------------------------------------------- /libfdo-data/build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, process::Command}; 2 | 3 | const VERSION_MAJOR: &str = env!("CARGO_PKG_VERSION_MAJOR"); 4 | const VERSION_MINOR: &str = env!("CARGO_PKG_VERSION_MINOR"); 5 | const VERSION_PATCH: &str = env!("CARGO_PKG_VERSION_PATCH"); 6 | const MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR"); 7 | const AUTOGEN_WARNING: &str = "/* This file is automatically generated, do not modify */"; 8 | 9 | fn main() { 10 | println!("cargo:rustc-cdylib-link-arg=-Wl,-soname,libfdo_data.so.{VERSION_MAJOR}"); 11 | 12 | cbindgen::Builder::new() 13 | .with_crate(MANIFEST_DIR) 14 | .with_parse_deps(true) 15 | .with_parse_include(&["fdo-data-formats"]) 16 | .with_include_version(false) 17 | .with_include_guard("FDO_DATA_H") 18 | .with_after_include(format!( 19 | "\n#define FDO_DATA_MAJOR {VERSION_MAJOR}\n#define FDO_DATA_MINOR {VERSION_MINOR}\n#define FDO_DATA_PATCH {VERSION_PATCH}" 20 | )) 21 | .with_pragma_once(true) 22 | .with_autogen_warning(AUTOGEN_WARNING) 23 | .with_style(cbindgen::Style::Both) 24 | .with_item_prefix("Fdo") 25 | .with_documentation(true) 26 | .with_language(cbindgen::Language::C) 27 | .generate() 28 | .expect("Unable to generate bindings") 29 | .write_to_file("fdo_data.h"); 30 | 31 | // Generate the Go bindings documentation 32 | let manifest_dir = std::path::PathBuf::from(MANIFEST_DIR); 33 | let godoc = Command::new("go") 34 | .args(["doc", "-all"]) 35 | .current_dir( 36 | manifest_dir 37 | .join("../libfdo-data-go") 38 | .canonicalize() 39 | .unwrap(), 40 | ) 41 | .output() 42 | .expect("Unable to generate Go bindings documentation"); 43 | let godoc = String::from_utf8(godoc.stdout).unwrap(); 44 | let godoc = format!( 45 | "{AUTOGEN_WARNING} 46 | /* 47 | * This file is intended to detect API changes during CI. 48 | * DO commit changes to this file. 49 | */ 50 | {godoc}" 51 | ); 52 | 53 | std::fs::write(manifest_dir.join("libfdo-data-go.doc"), godoc.as_bytes()) 54 | .expect("Unable to write Go bindings documentation"); 55 | } 56 | -------------------------------------------------------------------------------- /libfdo-data/fdo_data.h: -------------------------------------------------------------------------------- 1 | #ifndef FDO_DATA_H 2 | #define FDO_DATA_H 3 | 4 | #pragma once 5 | 6 | /* This file is automatically generated, do not modify */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define FDO_DATA_MAJOR 0 14 | #define FDO_DATA_MINOR 5 15 | #define FDO_DATA_PATCH 5 16 | 17 | typedef struct FdoOwnershipVoucher FdoOwnershipVoucher; 18 | 19 | /** 20 | * A list of Ownership Vouchers 21 | */ 22 | typedef struct FdoOwnershipVoucherList FdoOwnershipVoucherList; 23 | 24 | /** 25 | * Free a string returned by libfdo-data functions 26 | */ 27 | void fdo_free_string(char *s); 28 | 29 | /** 30 | * Returns a string describing the last error that occurred 31 | * 32 | * Note: The returned string ownership is transferred to the caller, and should 33 | * be freed with `fdo_free_string` 34 | */ 35 | char *fdo_get_last_error(void); 36 | 37 | /** 38 | * Returns a single Ownership Voucher from a list of Ownership Vouchers 39 | * 40 | * Note: the return Ownership Voucher is still owned by the list, and should 41 | * *NOT* be freed by the caller. 42 | * 43 | * Return value: 44 | * NULL if index is out of bounds 45 | * Pointer to an OwnershipVoucher on success 46 | */ 47 | const struct FdoOwnershipVoucher *fdo_ownershipvoucher_list_get(const struct FdoOwnershipVoucherList *list, 48 | uint64_t index); 49 | 50 | /** 51 | * Returns the length of an Ownership Voucher List 52 | */ 53 | uint64_t fdo_ownershipvoucher_list_len(const struct FdoOwnershipVoucherList *list); 54 | 55 | /** 56 | * Frees an Ownership Voucher List 57 | */ 58 | void fdo_ownershipvoucher_list_free(struct FdoOwnershipVoucherList *list); 59 | 60 | /** 61 | * Creates an Ownership Voucher List from raw data of appended vouchers 62 | * 63 | * Return value: 64 | * NULL on error (last error is set) 65 | * Pointer to an OwnershipVoucherList on success 66 | */ 67 | const struct FdoOwnershipVoucherList *fdo_ownershipvoucher_many_from_data(const void *data, 68 | size_t len); 69 | 70 | /** 71 | * Creates a new OwnershipVoucher from raw data 72 | * 73 | * Return value: 74 | * NULL on error (last error is set) 75 | * Pointer to an FdoOwnershipVoucher on success 76 | */ 77 | struct FdoOwnershipVoucher *fdo_ownershipvoucher_from_data(const void *data, size_t len); 78 | 79 | /** 80 | * Frees an OwnershipVoucher 81 | */ 82 | void fdo_ownershipvoucher_free(struct FdoOwnershipVoucher *v); 83 | 84 | /** 85 | * Returns the protocol version in the ownership voucher 86 | * 87 | * Return value: 88 | * -1 on error (last error is set) 89 | * protocol version on success 90 | */ 91 | int32_t fdo_ownershipvoucher_header_get_protocol_version(const struct FdoOwnershipVoucher *v); 92 | 93 | /** 94 | * Returns the GUID of the ownership voucher 95 | * 96 | * Return value: 97 | * NULL on error (last error is set) 98 | * Pointer to a string containing the GUID on success 99 | * 100 | * Note: The returned string ownership is transferred to the caller, and should 101 | * be freed with `fdo_free_string` 102 | */ 103 | const char *fdo_ownershipvoucher_header_get_guid(const struct FdoOwnershipVoucher *v); 104 | 105 | /** 106 | * Returns the device info of the ownership voucher if it is a string 107 | * 108 | * Return value: 109 | * NULL on error or if Device Info is not a string 110 | * Pointer to a string containing the Device Info on success 111 | * 112 | * Note: The returned string ownership is transferred to the caller, and should 113 | * be freed with `fdo_free_string` 114 | */ 115 | const char *fdo_ownershipvoucher_header_get_device_info_string(const struct FdoOwnershipVoucher *v); 116 | 117 | #endif /* FDO_DATA_H */ 118 | -------------------------------------------------------------------------------- /libfdo-data/libfdo-data-go.doc: -------------------------------------------------------------------------------- 1 | /* This file is automatically generated, do not modify */ 2 | /* 3 | * This file is intended to detect API changes during CI. 4 | * DO commit changes to this file. 5 | */ 6 | package libfdo_data // import "github.com/fedora-iot/fido-device-onboard-rs/libfdo-data-go" 7 | 8 | 9 | TYPES 10 | 11 | type OwnershipVoucher struct { 12 | // Has unexported fields. 13 | } 14 | 15 | func ParseOwnershipVoucher(voucherB []byte) (*OwnershipVoucher, error) 16 | 17 | func (ov *OwnershipVoucher) Free() 18 | 19 | func (ov *OwnershipVoucher) GetDeviceInfo() string 20 | 21 | func (ov *OwnershipVoucher) GetGUID() string 22 | 23 | func (ov *OwnershipVoucher) GetProtocolVersion() uint32 24 | 25 | type OwnershipVoucherList struct { 26 | // Has unexported fields. 27 | } 28 | 29 | func ParseManyOwnershipVouchers(voucherB []byte) (*OwnershipVoucherList, error) 30 | 31 | func (ovl *OwnershipVoucherList) Free() 32 | 33 | func (ovl *OwnershipVoucherList) GetVoucher(index int) (*OwnershipVoucher, error) 34 | 35 | func (ovl *OwnershipVoucherList) Len() int 36 | 37 | -------------------------------------------------------------------------------- /libfdo-data/src/lib.rs: -------------------------------------------------------------------------------- 1 | use libc::c_char; 2 | use std::ptr::addr_of; 3 | use std::{ffi::CString, ptr::null_mut}; 4 | 5 | #[cfg(test)] 6 | mod test_common; 7 | 8 | mod ownershipvoucher; 9 | 10 | static mut LAST_ERROR: Option = None; 11 | 12 | fn clear_last_error() { 13 | unsafe { 14 | LAST_ERROR = None; 15 | } 16 | } 17 | 18 | fn set_last_error(err: T) 19 | where 20 | T: ToString, 21 | { 22 | unsafe { 23 | LAST_ERROR = Some(err.to_string()); 24 | } 25 | } 26 | 27 | /// Free a string returned by libfdo-data functions 28 | #[no_mangle] 29 | #[allow(clippy::missing_safety_doc)] 30 | pub unsafe extern "C" fn fdo_free_string(s: *mut c_char) { 31 | clear_last_error(); 32 | 33 | if s.is_null() { 34 | return; 35 | } 36 | drop(CString::from_raw(s)); 37 | } 38 | 39 | /// Returns a string describing the last error that occurred 40 | /// 41 | /// Note: The returned string ownership is transferred to the caller, and should 42 | /// be freed with `fdo_free_string` 43 | #[no_mangle] 44 | #[allow(unused_unsafe)] 45 | pub extern "C" fn fdo_get_last_error() -> *mut c_char { 46 | let result = unsafe { addr_of!(LAST_ERROR) }; 47 | if result.is_null() { 48 | null_mut() 49 | } else { 50 | match unsafe { result.as_ref() } { 51 | None => null_mut(), 52 | Some(e) => CString::new(e.clone().unwrap().as_bytes()) 53 | .unwrap() 54 | .into_raw(), 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /libfdo-data/src/test_common.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io, 3 | path::{Path, PathBuf}, 4 | process::{Command, Output}, 5 | }; 6 | 7 | const ROOT_DIR: &str = env!("CARGO_MANIFEST_DIR"); 8 | fn root_dir() -> &'static Path { 9 | Path::new(ROOT_DIR) 10 | } 11 | 12 | pub trait OutputExt { 13 | fn stdout_equals(&self, expected: &str); 14 | fn stderr_equals(&self, expected: &str); 15 | } 16 | 17 | fn out_equals(text: &[u8], expected: &str) { 18 | let text = std::str::from_utf8(text).unwrap(); 19 | assert_str::assert_str_trim_eq!(text, expected,); 20 | } 21 | 22 | impl OutputExt for Output { 23 | fn stdout_equals(&self, expected: &str) { 24 | out_equals(&self.stdout, expected) 25 | } 26 | 27 | fn stderr_equals(&self, expected: &str) { 28 | out_equals(&self.stderr, expected) 29 | } 30 | } 31 | 32 | pub fn run_external(script: &str, args: &[&str]) -> Result { 33 | let descrip = format!("test script {}.go, with args {:?}", script, args); 34 | let script_path_location = root_dir().join("test_scripts"); 35 | let script_path = script_path_location.join(format!("{}.go", script)); 36 | 37 | // Try to remove the old symlink, handle error gracefully 38 | if let Err(e) = std::fs::remove_file(format!( 39 | "{}/../target/debug/libfdo_data.so.{}", 40 | ROOT_DIR, 41 | std::env::var("CARGO_PKG_VERSION_MAJOR").unwrap() 42 | )) { 43 | if e.kind() != io::ErrorKind::NotFound { 44 | return Err(format!("Failed to remove libfdo_data.so.0 symlink {:?}", e)); 45 | } 46 | } 47 | 48 | // Try to create a new symlink, handle error gracefully 49 | if let Err(e) = std::os::unix::fs::symlink( 50 | format!("{}/../target/debug/libfdo_data.so", ROOT_DIR), 51 | format!( 52 | "{}/../target/debug/libfdo_data.so.{}", 53 | ROOT_DIR, 54 | std::env::var("CARGO_PKG_VERSION_MAJOR").unwrap() 55 | ), 56 | ) { 57 | if e.kind() != io::ErrorKind::AlreadyExists { 58 | return Err(format!("Failed to create libfdo_data.so.0 symlink {:?}", e)); 59 | } 60 | } 61 | 62 | // Run the command, returning an error if it fails 63 | let result = Command::new("go") 64 | .arg("run") 65 | .args(["-tags", "localbuild"]) 66 | .arg(script_path) 67 | .args(args) 68 | .current_dir(&script_path_location) 69 | .output() 70 | .map_err(|_| format!("Failed to run {}", descrip))?; 71 | 72 | println!("Result of {}: {:?}", descrip, result); 73 | 74 | Ok(result) // Return the result as Ok if everything succeeded 75 | } 76 | 77 | pub fn test_asset_path(asset_name: &str) -> PathBuf { 78 | root_dir().join("test_assets").join(asset_name) 79 | } 80 | -------------------------------------------------------------------------------- /libfdo-data/test_assets/testdevice1.ov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fdo-rs/fido-device-onboard-rs/21bb4052c3cc589f244f09a223d887150d1fe6e1/libfdo-data/test_assets/testdevice1.ov -------------------------------------------------------------------------------- /libfdo-data/test_scripts/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/fedora-iot/fido-device-onboard-rs/libfdo-data/test_scripts 2 | 3 | go 1.16 4 | 5 | require github.com/fedora-iot/fido-device-onboard-rs/libfdo-data-go v0.0.0-00010101000000-000000000000 // indirect 6 | 7 | replace github.com/fedora-iot/fido-device-onboard-rs/libfdo-data-go => ../../libfdo-data-go 8 | 9 | -------------------------------------------------------------------------------- /libfdo-data/test_scripts/ownershipvoucher.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | 8 | libfdo_data "github.com/fedora-iot/fido-device-onboard-rs/libfdo-data-go" 9 | ) 10 | 11 | func main() { 12 | if len(os.Args) != 2 { 13 | fmt.Printf("Usage: %s \n", os.Args[0]) 14 | os.Exit(1) 15 | } 16 | ctsB, err := ioutil.ReadFile(os.Args[1]) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | voucher, err := libfdo_data.ParseOwnershipVoucher(ctsB) 22 | if err != nil { 23 | fmt.Println(err) 24 | return 25 | } 26 | defer voucher.Free() 27 | 28 | protocol_version := voucher.GetProtocolVersion() 29 | guid := voucher.GetGUID() 30 | device_info := voucher.GetDeviceInfo() 31 | 32 | fmt.Println("Protocol version:", protocol_version) 33 | fmt.Println("Device GUID:", guid) 34 | fmt.Println("Device Info:", device_info) 35 | } 36 | -------------------------------------------------------------------------------- /libfdo-data/test_scripts/ownershipvoucher_many.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | 8 | libfdo_data "github.com/fedora-iot/fido-device-onboard-rs/libfdo-data-go" 9 | ) 10 | 11 | func main() { 12 | if len(os.Args) < 2 { 13 | fmt.Printf("Usage: %s \n", os.Args[0]) 14 | os.Exit(1) 15 | } 16 | ctsB := []byte{} 17 | for _, arg := range os.Args[1:] { 18 | fileB, err := ioutil.ReadFile(arg) 19 | if err != nil { 20 | panic(err) 21 | } 22 | ctsB = append(ctsB, fileB...) 23 | } 24 | 25 | vouchers, err := libfdo_data.ParseManyOwnershipVouchers(ctsB) 26 | if err != nil { 27 | fmt.Println(err) 28 | return 29 | } 30 | defer vouchers.Free() 31 | 32 | for i := 0; i < vouchers.Len(); i++ { 33 | fmt.Println("Device", i) 34 | 35 | voucher, err := vouchers.GetVoucher(i) 36 | if err != nil { 37 | panic(err) 38 | } 39 | 40 | protocol_version := voucher.GetProtocolVersion() 41 | guid := voucher.GetGUID() 42 | device_info := voucher.GetDeviceInfo() 43 | 44 | fmt.Println("\tProtocol version:", protocol_version) 45 | fmt.Println("\tDevice GUID:", guid) 46 | fmt.Println("\tDevice Info:", device_info) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /manufacturing-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fdo-manufacturing-client" 3 | version = "0.5.5" 4 | authors = ["Patrick Uiterwijk "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1" 11 | clap = { version = "4.4", features = ["derive"] } 12 | hex = "0.4" 13 | log = "0.4" 14 | openssl = "0.10.72" 15 | tokio = { version = "1", features = ["full"] } 16 | rand = "0.8.4" 17 | tss-esapi = { version = "7.6", features = ["generate-bindings"] } 18 | regex = "1.11.1" 19 | 20 | fdo-data-formats = { path = "../data-formats", version = "0.5.5" } 21 | fdo-http-wrapper = { path = "../http-wrapper", version = "0.5.5", features = ["client"] } 22 | fdo-util = { path = "../util", version = "0.5.5" } 23 | clap_builder = "4.4" 24 | -------------------------------------------------------------------------------- /manufacturing-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fdo-manufacturing-server" 3 | version = "0.5.5" 4 | authors = ["Patrick Uiterwijk "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1" 11 | config = "0.13.4" 12 | tokio = { version = "1", features = ["full"] } 13 | thiserror= "1" 14 | serde = "1" 15 | openssl = "0.10.72" 16 | warp = "0.3.6" 17 | log = "0.4" 18 | hex = "0.4" 19 | serde_yaml = "0.9" 20 | tar = "0.4.41" 21 | flate2 = "1.0.31" 22 | tempfile = "3" 23 | 24 | fdo-data-formats = { path = "../data-formats", version = "0.5.5" } 25 | fdo-http-wrapper = { path = "../http-wrapper", version = "0.5.5", features = ["server"] } 26 | fdo-store = { path = "../store", version = "0.5.5", features = ["directory"] } 27 | fdo-util = { path = "../util", version = "0.5.5" } 28 | -------------------------------------------------------------------------------- /manufacturing-server/src/handlers/mod.rs: -------------------------------------------------------------------------------- 1 | pub(super) mod di; 2 | pub(super) mod diun; 3 | -------------------------------------------------------------------------------- /migrations/migrations_manufacturing_server_postgres/2023-10-03-152801_create_db/down.sql: -------------------------------------------------------------------------------- 1 | -- This file should undo anything in `up.sql` 2 | 3 | DROP TABLE manufacturer_vouchers; 4 | -------------------------------------------------------------------------------- /migrations/migrations_manufacturing_server_postgres/2023-10-03-152801_create_db/up.sql: -------------------------------------------------------------------------------- 1 | -- Your SQL goes here 2 | 3 | CREATE TABLE manufacturer_vouchers ( 4 | guid varchar(36) NOT NULL PRIMARY KEY, 5 | contents bytea NOT NULL, 6 | ttl bigint 7 | ); 8 | -------------------------------------------------------------------------------- /migrations/migrations_manufacturing_server_sqlite/2023-10-03-152801_create_db/down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE manufacturer_vouchers; 2 | -------------------------------------------------------------------------------- /migrations/migrations_manufacturing_server_sqlite/2023-10-03-152801_create_db/up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE manufacturer_vouchers ( 2 | guid varchar(36) NOT NULL PRIMARY KEY, 3 | contents blob NOT NULL, 4 | ttl bigint 5 | ); 6 | -------------------------------------------------------------------------------- /migrations/migrations_owner_onboarding_server_postgres/2023-10-03-152801_create_db/down.sql: -------------------------------------------------------------------------------- 1 | -- This file should undo anything in `up.sql` 2 | 3 | DROP TABLE owner_vouchers; 4 | -------------------------------------------------------------------------------- /migrations/migrations_owner_onboarding_server_postgres/2023-10-03-152801_create_db/up.sql: -------------------------------------------------------------------------------- 1 | -- Your SQL goes here 2 | 3 | CREATE TABLE owner_vouchers ( 4 | guid varchar(36) NOT NULL PRIMARY KEY, 5 | contents bytea NOT NULL, 6 | to2_performed boolean, 7 | to0_accept_owner_wait_seconds bigint 8 | ); 9 | -------------------------------------------------------------------------------- /migrations/migrations_owner_onboarding_server_sqlite/2023-10-03-152801_create_db/down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE owner_vouchers; 2 | -------------------------------------------------------------------------------- /migrations/migrations_owner_onboarding_server_sqlite/2023-10-03-152801_create_db/up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE owner_vouchers ( 2 | guid varchar(36) NOT NULL PRIMARY KEY, 3 | contents blob NOT NULL, 4 | to2_performed bool, 5 | to0_accept_owner_wait_seconds bigint 6 | ); 7 | -------------------------------------------------------------------------------- /migrations/migrations_rendezvous_server_postgres/2023-10-03-152801_create_db/down.sql: -------------------------------------------------------------------------------- 1 | -- This file should undo anything in `up.sql` 2 | 3 | DROP TABLE rendezvous_vouchers; 4 | -------------------------------------------------------------------------------- /migrations/migrations_rendezvous_server_postgres/2023-10-03-152801_create_db/up.sql: -------------------------------------------------------------------------------- 1 | -- Your SQL goes here 2 | 3 | CREATE TABLE rendezvous_vouchers ( 4 | guid varchar(36) NOT NULL PRIMARY KEY, 5 | contents bytea NOT NULL, 6 | ttl bigint 7 | ); 8 | -------------------------------------------------------------------------------- /migrations/migrations_rendezvous_server_sqlite/2023-10-03-152801_create_db/down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE rendezvous_vouchers; 2 | -------------------------------------------------------------------------------- /migrations/migrations_rendezvous_server_sqlite/2023-10-03-152801_create_db/up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE rendezvous_vouchers ( 2 | guid varchar(36) NOT NULL PRIMARY KEY, 3 | contents blob NOT NULL, 4 | ttl bigint 5 | ); 6 | -------------------------------------------------------------------------------- /owner-onboarding-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fdo-owner-onboarding-server" 3 | version = "0.5.5" 4 | authors = ["Patrick Uiterwijk "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1" 11 | config = "0.13.4" 12 | tokio = { version = "1", features = ["full"] } 13 | thiserror= "1" 14 | serde = "1" 15 | openssl = "0.10.72" 16 | warp = "0.3.6" 17 | serde_bytes = "0.11" 18 | serde_cbor = "0.11" 19 | log = "0.4" 20 | serde_yaml = "0.9" 21 | time = "0.3" 22 | hex = "0.4" 23 | 24 | fdo-data-formats = { path = "../data-formats", version = "0.5.5" } 25 | fdo-http-wrapper = { path = "../http-wrapper", version = "0.5.5", features = ["server", "client"] } 26 | fdo-store = { path = "../store", version = "0.5.5", features = ["directory"] } 27 | fdo-util = { path = "../util", version = "0.5.5" } 28 | -------------------------------------------------------------------------------- /owner-tool/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fdo-owner-tool" 3 | version = "0.5.5" 4 | authors = ["Patrick Uiterwijk "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1.0" 11 | clap = { version = "4.4", features = ["derive"] } 12 | log = "0.4" 13 | openssl = "0.10.72" 14 | serde = { version = "1", features = ["derive"] } 15 | serde_yaml = "0.9" 16 | tokio = { version = "1", features = ["full"] } 17 | tss-esapi = { version = "7.6", features = ["generate-bindings"] } 18 | reqwest = { version = "0.12.9", features = ["blocking"] } 19 | 20 | fdo-util = { path = "../util", version = "0.5.5" } 21 | fdo-data-formats = { path = "../data-formats", version = "0.5.5" } 22 | fdo-http-wrapper = { path = "../http-wrapper", version = "0.5.5", features = ["client"] } 23 | fdo-db = { path = "../db", version = "0.5.5"} 24 | 25 | hex = "0.4" 26 | clap_builder = "4.4" 27 | -------------------------------------------------------------------------------- /patches/0001-use-released-aws-nitro-enclaves-cose-version.patch: -------------------------------------------------------------------------------- 1 | diff --git a/Cargo.toml b/Cargo.toml 2 | index 5c239db2..a05cbe01 100644 3 | --- a/Cargo.toml 4 | +++ b/Cargo.toml 5 | @@ -38,6 +38,3 @@ default-members = [ 6 | ] 7 | 8 | resolver = "2" 9 | - 10 | -[patch.crates-io] 11 | -aws-nitro-enclaves-cose = { git = "https://github.com/awslabs/aws-nitro-enclaves-cose/", rev = "6064f826d551a9db0bd42e9cf928feaf272e8d17" } 12 | -------------------------------------------------------------------------------- /rendezvous-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fdo-rendezvous-server" 3 | version = "0.5.5" 4 | authors = ["Patrick Uiterwijk "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1" 11 | config = "0.13.4" 12 | tokio = { version = "1", features = ["full"] } 13 | thiserror= "1" 14 | serde = "1" 15 | openssl = "0.10.72" 16 | warp = "0.3.6" 17 | log = "0.4" 18 | time = "0.3" 19 | 20 | fdo-data-formats = { path = "../data-formats", version = "0.5.5" } 21 | fdo-http-wrapper = { path = "../http-wrapper", version = "0.5.5", features = ["server"] } 22 | fdo-store = { path = "../store", version = "0.5.5" } 23 | fdo-util = { path = "../util", version = "0.5.5" } 24 | -------------------------------------------------------------------------------- /serviceinfo-api-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fdo-serviceinfo-api-server" 3 | version = "0.5.5" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1" 10 | config = "0.13.4" 11 | hex = "0.4" 12 | tokio = { version = "1", features = ["full"] } 13 | warp = "0.3.6" 14 | log = "0.4" 15 | serde = "1" 16 | serde_bytes = "0.11" 17 | serde_json = "1" 18 | 19 | fdo-http-wrapper = { path = "../http-wrapper", version = "0.5.5", features = ["server"] } 20 | fdo-data-formats = { path = "../data-formats", version = "0.5.5" } 21 | fdo-store = { path = "../store", version = "0.5.5", features = ["directory"] } 22 | fdo-util = { path = "../util", version = "0.5.5" } 23 | -------------------------------------------------------------------------------- /store/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fdo-store" 3 | version = "0.5.5" 4 | authors = ["Patrick Uiterwijk "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = { version = "1", optional = true} 11 | 12 | fdo-data-formats = { path = "../data-formats", version = "0.5.5" } 13 | 14 | thiserror = "1" 15 | async-trait = "0.1" 16 | log = "0.4" 17 | serde = { version = "1", features = ["derive"] } 18 | time = "0.3" 19 | 20 | # feature-specific dependencies 21 | # directory 22 | xattr = { version = "1.0", default-features = false, optional = true } # We *need* xattrs to store TTL 23 | serde_cbor = { version = "0.11", optional = true } 24 | 25 | # database 26 | fdo-db = { path = "../db", version = "0.5.5"} 27 | 28 | diesel = { version = "2.2.7", features = ["sqlite", "postgres", "r2d2"], optional = true } 29 | 30 | [features] 31 | directory = ["xattr", "serde_cbor"] 32 | db = ["diesel", "anyhow"] 33 | 34 | default = ["directory", "db"] 35 | -------------------------------------------------------------------------------- /test/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | timeout = 30 3 | # human-readable stdout/stderr results display 4 | stdout_callback = yaml 5 | 6 | [ssh_connection] 7 | scp_if_ssh=True 8 | pipelining=False 9 | -------------------------------------------------------------------------------- /test/fdo/manufacturing-server.yml: -------------------------------------------------------------------------------- 1 | --- 2 | session_store_driver: 3 | Directory: 4 | path: /etc/fdo/stores/manufacturing_sessions 5 | ownership_voucher_store_driver: 6 | Directory: 7 | path: /etc/fdo/stores/owner_vouchers 8 | public_key_store_driver: 9 | Directory: 10 | path: /etc/fdo/stores/manufacturer_keys 11 | bind: "0.0.0.0:8080" 12 | protocols: 13 | plain_di: false 14 | diun: 15 | mfg_string_type: SerialNumber 16 | key_type: SECP384R1 17 | allowed_key_storage_types: 18 | - FileSystem 19 | key_path: /etc/fdo/keys/diun_key.der 20 | cert_path: /etc/fdo/keys/diun_cert.pem 21 | rendezvous_info: 22 | - deviceport: 8082 23 | ip_address: 192.168.200.52 24 | ownerport: 8082 25 | protocol: http 26 | manufacturing: 27 | manufacturer_cert_path: /etc/fdo/keys/manufacturer_cert.pem 28 | device_cert_ca_private_key: /etc/fdo/keys/device_ca_key.der 29 | device_cert_ca_chain: /etc/fdo/keys/device_ca_cert.pem 30 | owner_cert_path: /etc/fdo/keys/owner_cert.pem 31 | manufacturer_private_key: /etc/fdo/keys/manufacturer_key.der 32 | -------------------------------------------------------------------------------- /test/fdo/owner-onboarding-server.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ownership_voucher_store_driver: 3 | Directory: 4 | path: /etc/fdo/stores/owner_vouchers 5 | session_store_driver: 6 | Directory: 7 | path: /etc/fdo/stores/owner_onboarding_sessions 8 | trusted_device_keys_path: /etc/fdo/keys/device_ca_cert.pem 9 | owner_private_key_path: /etc/fdo/keys/owner_key.der 10 | owner_public_key_path: /etc/fdo/keys/owner_cert.pem 11 | bind: "0.0.0.0:8081" 12 | service_info_api_url: "http://192.168.200.53:8083/device_info" 13 | service_info_api_authentication: 14 | BearerToken: 15 | token: 2IOtlXsSqfcGjnhBLZjPiHIteskzZEW3lncRzpEmgqI= 16 | owner_addresses: 17 | - transport: http 18 | addresses: 19 | - ip_address: 192.168.200.51 20 | port: 8081 21 | report_to_rendezvous_endpoint_enabled: true 22 | -------------------------------------------------------------------------------- /test/fdo/rendezvous-server.yml: -------------------------------------------------------------------------------- 1 | --- 2 | storage_driver: 3 | Directory: 4 | path: /etc/fdo/stores/rendezvous_registered 5 | session_store_driver: 6 | Directory: 7 | path: /etc/fdo/stores/rendezvous_sessions 8 | trusted_manufacturer_keys_path: /etc/fdo/keys/manufacturer_cert.pem 9 | trusted_device_keys_path: /etc/fdo/keys/device_ca_cert.pem 10 | max_wait_seconds: ~ 11 | bind: "0.0.0.0:8082" 12 | -------------------------------------------------------------------------------- /test/fdo/serviceinfo-api-server.yml: -------------------------------------------------------------------------------- 1 | --- 2 | service_info: 3 | initial_user: null 4 | files: null 5 | commands: null 6 | diskencryption_clevis: null 7 | additional_serviceinfo: null 8 | bind: 0.0.0.0:8083 9 | service_info_auth_token: 2IOtlXsSqfcGjnhBLZjPiHIteskzZEW3lncRzpEmgqI= 10 | admin_auth_token: Va40bSkLcxwnfml1pmIuaWaOZG96mSMB6fu0xuzcueg= 11 | device_specific_store_driver: 12 | Directory: 13 | path: /etc/fdo/stores/serviceinfo_api_devices 14 | -------------------------------------------------------------------------------- /test/files/clients: -------------------------------------------------------------------------------- 1 | FROM quay.io/centos/centos:stream9 2 | ARG BUILDID 3 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-manufacturing-client /usr/local/bin 4 | COPY --from=fdo-build:${BUILDID} /usr/src/target/release/fdo-owner-tool /usr/local/bin 5 | RUN yum install -y postgresql libpq libpq-devel 6 | -------------------------------------------------------------------------------- /test/fmf/.fmf/version: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /test/fmf/plans/onboarding.fmf: -------------------------------------------------------------------------------- 1 | summary: Fido Device Onboarding Tests 2 | discover: 3 | how: fmf 4 | execute: 5 | how: tmt 6 | prepare: 7 | - how: shell 8 | script: dnf install -y postgresql-server sqlite 9 | - how: shell 10 | script: | 11 | echo "Adding missing SELinux permissions" 12 | tee /tmp/fdo-missing.cil < /dev/null 67 | 68 | # Set a customized dnsmasq configuration for libvirt so we always get the 69 | # same address on boot. 70 | greenprint "💡 Setup libvirt network" 71 | sudo tee /tmp/integration.xml > /dev/null << EOF 72 | 73 | integration 74 | 1c8fe98c-b53a-4ca4-bbdb-deb0f26b3579 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | EOF 92 | if ! sudo virsh net-info integration > /dev/null 2>&1; then 93 | sudo virsh net-define /tmp/integration.xml 94 | fi 95 | if [[ $(sudo virsh net-info integration | grep 'Active' | awk '{print $2}') == 'no' ]]; then 96 | sudo virsh net-start integration 97 | fi 98 | 99 | # Allow anyone in the wheel group to talk to libvirt. 100 | greenprint "🚪 Allowing users in wheel group to talk to libvirt" 101 | sudo tee /etc/polkit-1/rules.d/50-libvirt.rules > /dev/null << EOF 102 | polkit.addRule(function(action, subject) { 103 | if (action.id == "org.libvirt.unix.manage" && 104 | subject.isInGroup("adm")) { 105 | return polkit.Result.YES; 106 | } 107 | }); 108 | EOF 109 | 110 | # Reset selinux for /var/www/html/source 111 | sudo restorecon -Rv /var/www/html/source/ 112 | 113 | # Basic weldr API status checking 114 | sudo composer-cli status show 115 | 116 | # Source checking 117 | sudo composer-cli sources list 118 | for SOURCE in $(sudo composer-cli sources list); do 119 | sudo composer-cli sources info "$SOURCE" 120 | done 121 | -------------------------------------------------------------------------------- /util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fdo-util" 3 | version = "0.5.5" 4 | authors = ["Antonio Murdaca "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1" 11 | config = "0.13.4" 12 | glob = "0.3.1" 13 | log = "0.4" 14 | serde = "1" 15 | 16 | fdo-data-formats = { path = "../data-formats", version = "0.5.5" } 17 | fdo-store = { path = "../store", version = "0.5.5" } 18 | fdo-http-wrapper = { path = "../http-wrapper", version = "0.5.5", features = ["server", "client"] } 19 | serde_yaml = "0.9" 20 | serde_cbor = "0.11" 21 | serde_json = "1" 22 | -------------------------------------------------------------------------------- /util/src/device_credential_locations.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::path::Path; 4 | 5 | use anyhow::{anyhow, Context, Result}; 6 | 7 | use fdo_data_formats::{devicecredential::FileDeviceCredential, DeviceCredential, Serializable}; 8 | 9 | pub fn find() -> Option>> { 10 | let device_credential_locations: &[Box] = &[ 11 | Box::new(FileSystemPath { 12 | path: "/sys/firmware/qemu_fw_cfg/by_name/opt/device_onboarding/devicecredential/raw" 13 | .to_string(), 14 | deactivation_method: DeactivationMethod::None, 15 | }), 16 | Box::new(FileSystemPathEnv { 17 | env_var: "DEVICE_CREDENTIAL".to_string(), 18 | }), 19 | Box::new(FileSystemPath { 20 | path: "/etc/device-credentials".to_string(), 21 | deactivation_method: DeactivationMethod::Deactivate, 22 | }), 23 | ]; 24 | 25 | for devcredloc in device_credential_locations { 26 | log::trace!("Checking for device credential at {:?}", devcredloc); 27 | if let Some(v) = devcredloc.resolve() { 28 | log::trace!("Resolved to: {:?}", v); 29 | return Some(v); 30 | } 31 | } 32 | 33 | None 34 | } 35 | 36 | #[derive(Debug, Clone, Copy)] 37 | enum DeactivationMethod { 38 | None, 39 | Delete, 40 | Deactivate, 41 | } 42 | 43 | pub trait DeviceCredentialLocation: std::fmt::Debug { 44 | fn resolve(&self) -> Option>>; 45 | } 46 | 47 | pub trait UsableDeviceCredentialLocation: DeviceCredentialLocation { 48 | fn read(&self) -> Result>; 49 | fn deactivate(&self) -> Result<()>; 50 | } 51 | 52 | #[derive(Debug, Clone)] 53 | struct FileSystemPath { 54 | path: String, 55 | deactivation_method: DeactivationMethod, 56 | } 57 | 58 | impl DeviceCredentialLocation for FileSystemPath { 59 | fn resolve(&self) -> Option>> { 60 | if Path::new(&self.path).exists() { 61 | Some(Ok(Box::new(self.clone()))) 62 | } else { 63 | log::trace!("No (device credential) file exists at {}", &self.path); 64 | None 65 | } 66 | } 67 | } 68 | 69 | impl UsableDeviceCredentialLocation for FileSystemPath { 70 | fn read(&self) -> Result> { 71 | let contents = fs::read(&self.path) 72 | .with_context(|| format!("Error reading (device credential) file at {}", &self.path))?; 73 | let fdc = FileDeviceCredential::deserialize_data(&contents) 74 | .with_context(|| format!("Error parsing device credential from {}", &self.path))?; 75 | Ok(Box::new(fdc)) 76 | } 77 | 78 | fn deactivate(&self) -> Result<()> { 79 | match self.deactivation_method { 80 | DeactivationMethod::None => Ok(()), 81 | DeactivationMethod::Delete => fs::remove_file(&self.path) 82 | .with_context(|| format!("Error deleting file at {}", &self.path)), 83 | DeactivationMethod::Deactivate => self.perform_deactivation(), 84 | } 85 | } 86 | } 87 | 88 | impl FileSystemPath { 89 | fn perform_deactivation(&self) -> Result<()> { 90 | let contents = fs::read(&self.path) 91 | .with_context(|| format!("Error reading (device credential) file at {}", &self.path))?; 92 | let mut fdc = FileDeviceCredential::deserialize_data(&contents) 93 | .with_context(|| format!("Error parsing device credential from {}", &self.path))?; 94 | 95 | fdc.active = false; 96 | let new_dc_contents = fdc 97 | .serialize_data() 98 | .context("Error serializing deactivating device credential")?; 99 | self.write(new_dc_contents) 100 | .context("Error writing out new device credential for deactivation") 101 | } 102 | 103 | fn write(&self, new_contents: Vec) -> Result<()> { 104 | fs::write(&self.path, new_contents) 105 | .with_context(|| format!("Error writing to file at {}", &self.path)) 106 | } 107 | } 108 | 109 | #[derive(Debug)] 110 | struct FileSystemPathEnv { 111 | env_var: String, 112 | } 113 | 114 | impl DeviceCredentialLocation for FileSystemPathEnv { 115 | fn resolve(&self) -> Option>> { 116 | let env_val = match env::var_os(&self.env_var) { 117 | None => return None, 118 | Some(v) => match v.into_string() { 119 | Ok(s) => s, 120 | Err(_) => return Some(Err(anyhow!("Invalid environment variable value"))), 121 | }, 122 | }; 123 | let deactivation_method = match env::var_os(format!("{}_DELETE", &self.env_var)) { 124 | None => match env::var_os(format!("{}_DEACTIVATE", &self.env_var)) { 125 | None => DeactivationMethod::None, 126 | Some(_) => DeactivationMethod::Deactivate, 127 | }, 128 | Some(_) => DeactivationMethod::Delete, 129 | }; 130 | log::trace!( 131 | "Resolved environment variable {} to filesystem path {} (deactivation method {:?})", 132 | &self.env_var, 133 | &env_val, 134 | &deactivation_method, 135 | ); 136 | 137 | FileSystemPath { 138 | path: env_val, 139 | deactivation_method, 140 | } 141 | .resolve() 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /util/src/device_identification.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{bail, Result}; 2 | 3 | /// Checks whether the given string is sound to be a device identifier 4 | // The device identification string ends up in the CN field of the X509 5 | // certificate, which allows up to 64 characters. 6 | // See https://github.com/fedora-iot/fido-device-onboard-rs/issues/447 7 | pub fn check_device_identifier(identifier: &String) -> Result<()> { 8 | if identifier.len() > 64 { 9 | bail!(format!("{identifier} has more than 64 characters")); 10 | } 11 | Ok(()) 12 | } 13 | -------------------------------------------------------------------------------- /util/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod device_credential_locations; 2 | pub mod device_identification; 3 | pub mod passwd_shadow; 4 | pub mod servers; 5 | pub mod system_info; 6 | 7 | pub fn maybe_print_version( 8 | name: &'static str, 9 | major: &'static str, 10 | minor: &'static str, 11 | patch: &'static str, 12 | pre: &'static str, 13 | ) { 14 | let mut args = std::env::args(); 15 | if args.len() == 2 && args.nth(1).unwrap() == "--version" { 16 | println!("{name} {major}.{minor}.{patch} {pre}"); 17 | if !fdo_data_formats::interoperable_kdf_available() { 18 | println!("WARNING: This version of {name} is not interoperable with FDO as it is using a non-interoperable KDF implementation"); 19 | } 20 | std::process::exit(0); 21 | } 22 | } 23 | 24 | #[macro_export] 25 | macro_rules! add_version { 26 | () => { 27 | fdo_util::maybe_print_version( 28 | env!("CARGO_PKG_NAME"), 29 | env!("CARGO_PKG_VERSION_MAJOR"), 30 | env!("CARGO_PKG_VERSION_MINOR"), 31 | env!("CARGO_PKG_VERSION_PATCH"), 32 | env!("CARGO_PKG_VERSION_PRE"), 33 | ); 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /util/src/passwd_shadow.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{bail, Result}; 2 | use std::fs; 3 | 4 | /// Checks whether an entry for the given user name 'username' exists in 5 | /// /etc/passwd 6 | pub fn is_user_in_passwd(username: &str) -> Result { 7 | for line in fs::read_to_string("/etc/passwd")?.lines() { 8 | let contents: Vec<&str> = line.split(':').collect(); 9 | if !contents.is_empty() && contents[0] == username { 10 | return Ok(true); 11 | } 12 | } 13 | Ok(false) 14 | } 15 | 16 | /// Returns the uid, gid and home of the given user or Error if user does not 17 | /// exist in /etc/passwd. 18 | pub fn get_user_uid_gid_home(username: &str) -> Result<(u32, u32, String)> { 19 | for line in fs::read_to_string("/etc/passwd")?.lines() { 20 | let contents: Vec<&str> = line.split(':').collect(); 21 | if !contents.is_empty() && contents[0] == username { 22 | return Ok(( 23 | contents[2].parse::()?, 24 | contents[3].parse::()?, 25 | contents[5].to_string(), 26 | )); 27 | } 28 | } 29 | bail!("User {username} not found") 30 | } 31 | 32 | /// Returns the password of a given user. Errors if the user does not exist 33 | /// in /etc/shadow. 34 | pub fn get_user_passwd(username: &str) -> Result { 35 | for line in fs::read_to_string("/etc/shadow")?.lines() { 36 | let contents: Vec<&str> = line.split(':').collect(); 37 | if !contents.is_empty() && contents[0] == username { 38 | return Ok(contents[1].to_string()); 39 | } 40 | } 41 | bail!("User {username} not found") 42 | } 43 | -------------------------------------------------------------------------------- /util/src/servers/configuration/manufacturing_server.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use fdo_data_formats::constants::{KeyStorageType, MfgStringType, PublicKeyType}; 4 | use fdo_store::StoreConfig; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | use super::{AbsolutePathBuf, Bind}; 8 | 9 | #[derive(Debug, Serialize, Deserialize)] 10 | pub struct ManufacturingServerSettings { 11 | // Session store info 12 | #[serde(with = "serde_yaml::with::singleton_map")] 13 | pub session_store_driver: StoreConfig, 14 | 15 | // Ownership Voucher store info 16 | #[serde(with = "serde_yaml::with::singleton_map")] 17 | pub ownership_voucher_store_driver: StoreConfig, 18 | 19 | // Public key store info 20 | #[serde(with = "serde_yaml::with::singleton_map")] 21 | pub public_key_store_driver: Option, 22 | 23 | // Bind information 24 | pub bind: Bind, 25 | 26 | pub protocols: ProtocolSetting, 27 | 28 | pub rendezvous_info: Vec>, 29 | 30 | pub manufacturing: ManufacturingSettings, 31 | } 32 | 33 | #[derive(Debug, Serialize, Deserialize)] 34 | pub struct ManufacturingSettings { 35 | pub manufacturer_cert_path: AbsolutePathBuf, 36 | pub device_cert_ca_private_key: AbsolutePathBuf, 37 | pub device_cert_ca_chain: AbsolutePathBuf, 38 | 39 | pub owner_cert_path: Option, 40 | pub manufacturer_private_key: Option, 41 | } 42 | 43 | #[derive(Debug, Serialize, Deserialize)] 44 | pub struct ProtocolSetting { 45 | pub plain_di: Option, 46 | pub diun: Option, 47 | } 48 | 49 | #[derive(Debug, Serialize, Deserialize)] 50 | pub struct DiunSettings { 51 | pub mfg_string_type: MfgStringTypeString, 52 | 53 | pub key_type: PublicKeyTypeString, 54 | pub allowed_key_storage_types: Vec, 55 | 56 | pub key_path: AbsolutePathBuf, 57 | pub cert_path: AbsolutePathBuf, 58 | } 59 | 60 | #[derive(Debug, Serialize, Deserialize, Clone, Copy)] 61 | pub enum MfgStringTypeString { 62 | SerialNumber, 63 | MACAddress, 64 | } 65 | 66 | impl From for MfgStringType { 67 | fn from(mfg_string_type: MfgStringTypeString) -> Self { 68 | match mfg_string_type { 69 | MfgStringTypeString::SerialNumber => MfgStringType::SerialNumber, 70 | MfgStringTypeString::MACAddress => MfgStringType::MACAddress, 71 | } 72 | } 73 | } 74 | 75 | #[derive(Debug, Serialize, Deserialize, Clone, Copy)] 76 | pub enum PublicKeyTypeString { 77 | SECP256R1, 78 | SECP384R1, 79 | } 80 | 81 | impl From for PublicKeyType { 82 | fn from(key_type: PublicKeyTypeString) -> Self { 83 | match key_type { 84 | PublicKeyTypeString::SECP256R1 => PublicKeyType::SECP256R1, 85 | PublicKeyTypeString::SECP384R1 => PublicKeyType::SECP384R1, 86 | } 87 | } 88 | } 89 | 90 | #[derive(Debug, Serialize, Deserialize, Clone, Copy)] 91 | pub enum KeyStorageTypeString { 92 | FileSystem, 93 | Tpm, 94 | } 95 | 96 | impl From for KeyStorageType { 97 | fn from(key_type: KeyStorageTypeString) -> Self { 98 | match key_type { 99 | KeyStorageTypeString::FileSystem => KeyStorageType::FileSystem, 100 | KeyStorageTypeString::Tpm => KeyStorageType::Tpm, 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /util/src/servers/configuration/mod.rs: -------------------------------------------------------------------------------- 1 | //! Please note that the structures in this module are not stable 2 | 3 | pub mod manufacturing_server; 4 | pub mod owner_onboarding_server; 5 | pub mod rendezvous_server; 6 | pub mod serviceinfo_api_server; 7 | 8 | use std::{ 9 | net::SocketAddr, 10 | path::{Path, PathBuf}, 11 | }; 12 | 13 | use serde::{Deserialize, Serialize}; 14 | 15 | #[derive(Clone, Debug)] 16 | pub struct Bind(SocketAddr); 17 | 18 | impl Bind { 19 | pub fn new(addr: SocketAddr) -> Self { 20 | Self(addr) 21 | } 22 | } 23 | 24 | impl Serialize for Bind { 25 | fn serialize(&self, serializer: S) -> Result 26 | where 27 | S: serde::Serializer, 28 | { 29 | serializer.serialize_str(&self.0.to_string()) 30 | } 31 | } 32 | 33 | impl<'de> Deserialize<'de> for Bind { 34 | fn deserialize(deserializer: D) -> Result 35 | where 36 | D: serde::Deserializer<'de>, 37 | { 38 | let s = String::deserialize(deserializer)?; 39 | if s.is_empty() { 40 | return Err(serde::de::Error::custom("bind is empty".to_string())); 41 | } 42 | let parsed = s.parse::(); 43 | parsed 44 | .map(Bind) 45 | .map_err(|e| serde::de::Error::custom(format!("Error parsing bind string: {e:?}"))) 46 | } 47 | } 48 | 49 | impl std::fmt::Display for Bind { 50 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 51 | std::fmt::Debug::fmt(&self.0, f) 52 | } 53 | } 54 | 55 | impl AsRef for Bind { 56 | fn as_ref(&self) -> &SocketAddr { 57 | &self.0 58 | } 59 | } 60 | 61 | impl From for SocketAddr { 62 | fn from(bind: Bind) -> Self { 63 | bind.0 64 | } 65 | } 66 | 67 | #[derive(Debug)] 68 | pub struct AbsolutePathBuf(PathBuf); 69 | 70 | impl AbsolutePathBuf { 71 | pub fn new(path: PathBuf) -> Option { 72 | if !path.is_absolute() { 73 | None 74 | } else { 75 | Some(Self(path)) 76 | } 77 | } 78 | } 79 | 80 | impl Serialize for AbsolutePathBuf { 81 | fn serialize(&self, serializer: S) -> Result 82 | where 83 | S: serde::Serializer, 84 | { 85 | serializer.serialize_str(&self.0.to_string_lossy()) 86 | } 87 | } 88 | 89 | impl<'de> Deserialize<'de> for AbsolutePathBuf { 90 | fn deserialize(deserializer: D) -> Result 91 | where 92 | D: serde::Deserializer<'de>, 93 | { 94 | let s = String::deserialize(deserializer)?; 95 | if s.is_empty() { 96 | return Err(serde::de::Error::custom("path is empty".to_string())); 97 | } 98 | let path = PathBuf::from(&s); 99 | if !path.is_absolute() { 100 | return Err(serde::de::Error::custom(format!( 101 | "path {s} is not absolute" 102 | ))); 103 | } 104 | Ok(AbsolutePathBuf(path)) 105 | } 106 | } 107 | 108 | impl std::fmt::Display for AbsolutePathBuf { 109 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 110 | std::fmt::Debug::fmt(&self.0, f) 111 | } 112 | } 113 | 114 | impl AsRef for AbsolutePathBuf { 115 | fn as_ref(&self) -> &Path { 116 | &self.0 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /util/src/servers/configuration/owner_onboarding_server.rs: -------------------------------------------------------------------------------- 1 | use fdo_data_formats::types::RemoteConnection; 2 | use fdo_store::StoreConfig; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use super::{AbsolutePathBuf, Bind}; 6 | 7 | #[derive(Debug, Serialize, Deserialize)] 8 | pub struct OwnerOnboardingServerSettings { 9 | // Ownership Voucher storage info 10 | #[serde(with = "serde_yaml::with::singleton_map")] 11 | pub ownership_voucher_store_driver: StoreConfig, 12 | 13 | // Session store info 14 | #[serde(with = "serde_yaml::with::singleton_map")] 15 | pub session_store_driver: StoreConfig, 16 | 17 | // Trusted keys 18 | pub trusted_device_keys_path: Option, 19 | 20 | // Our private owner key 21 | pub owner_private_key_path: AbsolutePathBuf, 22 | pub owner_public_key_path: AbsolutePathBuf, 23 | 24 | // Bind information 25 | pub bind: Bind, 26 | 27 | // Service Info API Server 28 | pub service_info_api_url: String, 29 | #[serde(with = "serde_yaml::with::singleton_map")] 30 | pub service_info_api_authentication: fdo_http_wrapper::client::JsonAuthentication, 31 | 32 | pub owner_addresses: Vec, 33 | 34 | pub report_to_rendezvous_endpoint_enabled: bool, 35 | 36 | pub ov_registration_period: Option, 37 | pub ov_re_registration_window: Option, 38 | } 39 | 40 | // 10 minutes 41 | pub const DEFAULT_REGISTRATION_PERIOD: u32 = 600; 42 | // ~1 minute 43 | pub const DEFAULT_RE_REGISTRATION_WINDOW: u32 = 61; 44 | -------------------------------------------------------------------------------- /util/src/servers/configuration/rendezvous_server.rs: -------------------------------------------------------------------------------- 1 | use fdo_store::StoreConfig; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use super::{AbsolutePathBuf, Bind}; 5 | 6 | #[derive(Debug, Serialize, Deserialize)] 7 | pub struct RendezvousServerSettings { 8 | // Storage info 9 | #[serde(with = "serde_yaml::with::singleton_map")] 10 | pub storage_driver: StoreConfig, 11 | 12 | // Session store info 13 | #[serde(with = "serde_yaml::with::singleton_map")] 14 | pub session_store_driver: StoreConfig, 15 | 16 | // Trusted manufacturer public keys 17 | pub trusted_manufacturer_keys_path: Option, 18 | 19 | // Trusted CA certs for device cert chain verification 20 | pub trusted_device_keys_path: Option, 21 | 22 | // Other info 23 | pub max_wait_seconds: Option, 24 | 25 | // Bind information 26 | pub bind: Bind, 27 | } 28 | -------------------------------------------------------------------------------- /util/src/servers/configuration/serviceinfo_api_server.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use fdo_data_formats::constants::ServiceInfoModule; 4 | use fdo_store::StoreConfig; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | use super::Bind; 8 | 9 | #[derive(Debug, Serialize, Deserialize)] 10 | pub struct ServiceInfoApiServerSettings { 11 | pub service_info: ServiceInfoSettings, 12 | pub bind: Bind, 13 | 14 | pub service_info_auth_token: Option, 15 | pub admin_auth_token: Option, 16 | 17 | #[serde(with = "serde_yaml::with::singleton_map")] 18 | pub device_specific_store_driver: StoreConfig, 19 | } 20 | 21 | #[derive(Debug, Serialize, Deserialize, Clone)] 22 | #[serde(deny_unknown_fields)] 23 | pub struct ServiceInfoSettings { 24 | pub initial_user: Option, 25 | 26 | pub files: Option>, 27 | 28 | pub commands: Option>, 29 | 30 | pub diskencryption_clevis: Option>, 31 | 32 | pub additional_serviceinfo: Option>>, 33 | 34 | pub after_onboarding_reboot: Option, 35 | } 36 | 37 | #[derive(Debug, Serialize, Deserialize, Clone)] 38 | pub struct ServiceInfoDiskEncryptionClevisBinding { 39 | pub pin: String, 40 | pub config: String, 41 | } 42 | 43 | #[derive(Debug, Serialize, Deserialize, Clone)] 44 | pub struct ServiceInfoDiskEncryptionClevis { 45 | pub disk_label: String, 46 | pub binding: ServiceInfoDiskEncryptionClevisBinding, 47 | pub reencrypt: bool, 48 | } 49 | 50 | #[derive(Debug, Serialize, Deserialize, Clone)] 51 | pub struct ServiceInfoFile { 52 | pub path: String, 53 | pub permissions: Option, 54 | #[serde(skip)] 55 | pub parsed_permissions: Option, 56 | #[serde(skip)] 57 | pub contents_len: usize, 58 | #[serde(skip)] 59 | pub contents_hex: String, 60 | #[serde(skip)] 61 | pub hash_hex: String, 62 | pub source_path: String, 63 | } 64 | 65 | #[derive(Debug, Serialize, Deserialize, Clone)] 66 | pub struct ServiceInfoCommand { 67 | pub command: String, 68 | pub args: Vec, 69 | #[serde(default)] 70 | pub may_fail: bool, 71 | #[serde(default)] 72 | pub return_stdout: bool, 73 | #[serde(default)] 74 | pub return_stderr: bool, 75 | } 76 | 77 | #[derive(Debug, Serialize, Deserialize, Clone)] 78 | pub struct ServiceInfoInitialUser { 79 | pub username: String, 80 | pub password: Option, 81 | pub sshkeys: Option>, 82 | } 83 | -------------------------------------------------------------------------------- /util/src/system_info.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | pub fn get_current_user_name() -> String { 4 | String::from_utf8( 5 | Command::new("id") 6 | .arg("-u") 7 | .arg("-n") 8 | .output() 9 | .expect("Unable to run `id` command") 10 | .stdout, 11 | ) 12 | .expect("Unable to read current user name") 13 | .trim() 14 | .to_string() 15 | } 16 | --------------------------------------------------------------------------------