├── .github ├── .cSpellWords.txt ├── CONTRIBUTING.md ├── memory_statistics_config.json ├── pull_request_template.md └── workflows │ ├── ci.yml │ ├── doxygen-generation.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── LICENSE ├── MISRA.md ├── README.md ├── SECURITY.md ├── cspell.config.yaml ├── docs ├── doxygen │ ├── config.doxyfile │ ├── include │ │ └── size_table.md │ ├── layout.xml │ ├── pages.dox │ ├── porting.dox │ └── style.css └── plantuml │ ├── fleet_provisioning_operations.pu │ └── images │ └── fleet_provisioning_operations.png ├── fleetprovisioningFilePaths.cmake ├── manifest.yml ├── source ├── fleet_provisioning.c └── include │ ├── fleet_provisioning.h │ └── fleet_provisioning_config_defaults.h ├── test ├── CMakeLists.txt ├── cbmc │ ├── .gitignore │ ├── include │ │ └── README.md │ ├── proofs │ │ ├── FleetProvisioning_GetRegisterThingTopic │ │ │ ├── FleetProvisioning_GetRegisterThingTopic_harness.c │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── cbmc-proof.txt │ │ │ └── cbmc-viewer.json │ │ ├── FleetProvisioning_MatchTopic │ │ │ ├── FleetProvisioning_MatchTopic_harness.c │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── cbmc-proof.txt │ │ │ └── cbmc-viewer.json │ │ ├── Makefile-project-defines │ │ ├── Makefile-project-targets │ │ ├── Makefile-project-testing │ │ ├── Makefile-template-defines │ │ ├── Makefile.common │ │ ├── README.md │ │ ├── lib │ │ │ ├── __init__.py │ │ │ ├── print_tool_versions.py │ │ │ └── summarize.py │ │ ├── prepare.py │ │ └── run-cbmc-proofs.py │ ├── sources │ │ └── README.md │ └── stubs │ │ └── README.md ├── include │ └── fleet_provisioning_config.h └── unit-test │ ├── CMakeLists.txt │ ├── fleet_provisioning_utest.c │ └── unity_build.cmake └── tools ├── coverity ├── README.md └── misra.config └── unity ├── coverage.cmake ├── create_test.cmake └── project.yml /.github/.cSpellWords.txt: -------------------------------------------------------------------------------- 1 | cbmc 2 | CBMC 3 | cbor 4 | CBOR 5 | cmock 6 | Cmock 7 | CMock 8 | CMOCK 9 | coremqtt 10 | coverity 11 | Coverity 12 | CSDK 13 | ctest 14 | DCMOCK 15 | DCOV 16 | DDisable 17 | decihours 18 | Decihours 19 | DECIHOURS 20 | DNDEBUG 21 | DUNITY 22 | getpacketid 23 | isystem 24 | lcov 25 | misra 26 | Misra 27 | MISRA 28 | MQTT 29 | mypy 30 | nondet 31 | Nondet 32 | NONDET 33 | pylint 34 | pytest 35 | pyyaml 36 | sinclude 37 | UNACKED 38 | unpadded 39 | Unpadded 40 | UNPADDED 41 | UNSUB 42 | UNSUBACK 43 | unsubscriptions 44 | utest 45 | vect 46 | Vect 47 | VECT 48 | Wextra 49 | Wunused 50 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/issues), or [recently closed](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/issues?q=is%3Aissue+is%3Aclosed) issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 1. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 1. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 1. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 1. Ensure that your contributions conform to the [style guide](https://docs.aws.amazon.com/embedded-csdk/202011.00/lib-ref/docs/doxygen/output/html/guide_developer_styleguide.html). 35 | 1. Format your code with uncrustify, using the config available in [FreeRTOS/CI-CD-Github-Actions](https://github.com/FreeRTOS/CI-CD-Github-Actions/blob/main/formatting/uncrustify.cfg). 36 | 1. Ensure local tests pass. 37 | 1. Commit to your fork using clear commit messages. 38 | 1. Send us a pull request, answering any default questions in the pull request interface. 39 | 1. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 40 | 41 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 42 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 43 | 44 | 45 | ## Finding contributions to work on 46 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/labels?q=help+wanted) issues is a great place to start. 47 | 48 | 49 | ## Code of Conduct 50 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 51 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 52 | opensource-codeofconduct@amazon.com with any additional questions or comments. 53 | 54 | 55 | ## Security issue notifications 56 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](https://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public Github issue. 57 | 58 | 59 | ## Licensing 60 | 61 | See the [LICENSE](../LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 62 | 63 | We may ask you to sign a [Contributor License Agreement (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 64 | -------------------------------------------------------------------------------- /.github/memory_statistics_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib_name": "AWS IoT Fleet Provisioning", 3 | "src": [ 4 | "source/fleet_provisioning.c" 5 | ], 6 | "include": [ 7 | "source/include" 8 | ], 9 | "compiler_flags": [ 10 | "FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license. 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI Checks 2 | on: 3 | push: 4 | branches: ["**"] 5 | pull_request: 6 | branches: [main] 7 | workflow_dispatch: 8 | jobs: 9 | unittest: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Clone This Repo 13 | uses: actions/checkout@v3 14 | - name: Build 15 | run: | 16 | sudo apt-get install -y lcov 17 | cmake -S test -B build/ \ 18 | -G "Unix Makefiles" \ 19 | -DCMAKE_BUILD_TYPE=Debug \ 20 | -DBUILD_CLONE_SUBMODULES=ON \ 21 | -DUNITTEST=1 \ 22 | -DCMAKE_C_FLAGS='--coverage -Wall -Wextra -Werror -DNDEBUG' 23 | make -C build/ all 24 | - name: Test 25 | run: | 26 | cd build/ 27 | ctest -E system --output-on-failure 28 | cd .. 29 | - name: Run Coverage 30 | run: | 31 | make -C build/ coverage 32 | declare -a EXCLUDE=("\*test\*" "\*CMakeCCompilerId\*" "\*mocks\*") 33 | echo ${EXCLUDE[@]} | xargs lcov --rc lcov_branch_coverage=1 -r build/coverage.info -o build/coverage.info 34 | lcov --rc lcov_branch_coverage=1 --list build/coverage.info 35 | - name: Check Coverage 36 | uses: FreeRTOS/CI-CD-Github-Actions/coverage-cop@main 37 | with: 38 | coverage-file: ./build/coverage.info 39 | branch-coverage-min: 100 40 | line-coverage-min: 100 41 | build-with-default-config: 42 | runs-on: ubuntu-latest 43 | steps: 44 | - name: Clone This Repo 45 | uses: actions/checkout@v3 46 | - name: Build 47 | run: | 48 | cmake -S test -B build/ \ 49 | -G "Unix Makefiles" \ 50 | -DCMAKE_BUILD_TYPE=Debug \ 51 | -DCMAKE_C_FLAGS='-Wall -Wextra -Werror -DFLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG' 52 | make -C build/ all 53 | complexity: 54 | runs-on: ubuntu-latest 55 | steps: 56 | - uses: actions/checkout@v3 57 | - name: Check complexity 58 | uses: FreeRTOS/CI-CD-Github-Actions/complexity@main 59 | with: 60 | path: ./ 61 | doxygen: 62 | runs-on: ubuntu-latest 63 | steps: 64 | - uses: actions/checkout@v3 65 | - name: Run doxygen build 66 | uses: FreeRTOS/CI-CD-Github-Actions/doxygen@main 67 | with: 68 | path: ./ 69 | spell-check: 70 | runs-on: ubuntu-latest 71 | steps: 72 | - name: Clone This Repo 73 | uses: actions/checkout@v3 74 | - name: Run spellings check 75 | uses: FreeRTOS/CI-CD-Github-Actions/spellings@main 76 | with: 77 | path: ./ 78 | formatting: 79 | runs-on: ubuntu-20.04 80 | steps: 81 | - uses: actions/checkout@v3 82 | - name: Check formatting 83 | uses: FreeRTOS/CI-CD-Github-Actions/formatting@main 84 | with: 85 | path: ./ 86 | git-secrets: 87 | runs-on: ubuntu-latest 88 | steps: 89 | - uses: actions/checkout@v3 90 | - name: Checkout awslabs/git-secrets 91 | uses: actions/checkout@v3 92 | with: 93 | repository: awslabs/git-secrets 94 | ref: master 95 | path: git-secrets 96 | - name: Install git-secrets 97 | run: cd git-secrets && sudo make install && cd .. 98 | - name: Run git-secrets 99 | run: | 100 | git-secrets --register-aws 101 | git-secrets --scan 102 | memory_statistics: 103 | runs-on: ubuntu-latest 104 | steps: 105 | - uses: actions/checkout@v3 106 | with: 107 | submodules: "recursive" 108 | - name: Measure sizes 109 | uses: FreeRTOS/CI-CD-Github-Actions/memory_statistics@main 110 | with: 111 | config: .github/memory_statistics_config.json 112 | check_against: docs/doxygen/include/size_table.md 113 | 114 | link-verifier: 115 | runs-on: ubuntu-latest 116 | steps: 117 | - uses: actions/checkout@v3 118 | - name: Check Links 119 | env: 120 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 121 | uses: FreeRTOS/CI-CD-Github-Actions/link-verifier@main 122 | 123 | verify-manifest: 124 | runs-on: ubuntu-latest 125 | steps: 126 | - uses: actions/checkout@v3 127 | with: 128 | submodules: true 129 | fetch-depth: 0 130 | 131 | - name: Run manifest verifier 132 | uses: FreeRTOS/CI-CD-GitHub-Actions/manifest-verifier@main 133 | with: 134 | path: ./ 135 | fail-on-incorrect-version: true 136 | 137 | proof_ci: 138 | runs-on: cbmc_ubuntu-latest_16-core 139 | steps: 140 | - name: Set up CBMC runner 141 | uses: FreeRTOS/CI-CD-Github-Actions/set_up_cbmc_runner@main 142 | with: 143 | cbmc_version: "6.1.1" 144 | - name: Run CBMC 145 | uses: FreeRTOS/CI-CD-Github-Actions/run_cbmc@main 146 | with: 147 | proofs_dir: test/cbmc/proofs 148 | -------------------------------------------------------------------------------- /.github/workflows/doxygen-generation.yml: -------------------------------------------------------------------------------- 1 | name: Doxygen Generation 2 | on: 3 | push: 4 | branches: [main] 5 | workflow_dispatch: 6 | jobs: 7 | doxygen-generation: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Doxygen generation 11 | uses: FreeRTOS/CI-CD-Github-Actions/doxygen-generation@main 12 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release automation 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | commit_id: 7 | description: 'Commit ID to tag and create a release for' 8 | required: true 9 | version_number: 10 | description: 'Release Version Number (Eg, v1.0.0)' 11 | required: true 12 | 13 | jobs: 14 | tag-commit: 15 | name: Generate SBOM and tag commit 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | with: 21 | ref: ${{ github.event.inputs.commit_id }} 22 | - name: Configure git identity 23 | run: | 24 | git config --global user.name ${{ github.actor }} 25 | git config --global user.email ${{ github.actor }}@users.noreply.github.com 26 | - name: create a new branch that references commit id 27 | run: git checkout -b ${{ github.event.inputs.version_number }} ${{ github.event.inputs.commit_id }} 28 | - name: Generate SBOM 29 | uses: FreeRTOS/CI-CD-Github-Actions/sbom-generator@main 30 | with: 31 | repo_path: ./ 32 | source_path: ./source 33 | - name: commit SBOM file 34 | run: | 35 | git add . 36 | git commit -m 'Update SBOM' 37 | git push -u origin ${{ github.event.inputs.version_number }} 38 | - name: Tag Commit and Push to remote 39 | run: | 40 | git tag ${{ github.event.inputs.version_number }} -a -m "AWS IoT Fleet Provisioning ${{ github.event.inputs.version_number }}" 41 | git push origin --tags 42 | - name: Verify tag on remote 43 | run: | 44 | git tag -d ${{ github.event.inputs.version_number }} 45 | git remote update 46 | git checkout tags/${{ github.event.inputs.version_number }} 47 | git diff ${{ github.event.inputs.commit_id }} tags/${{ github.event.inputs.version_number }} 48 | create-zip: 49 | needs: tag-commit 50 | name: Create ZIP and verify package for release asset. 51 | runs-on: ubuntu-latest 52 | steps: 53 | - name: Install ZIP tools 54 | run: sudo apt-get install zip unzip 55 | - name: Checkout code 56 | uses: actions/checkout@v4 57 | with: 58 | ref: ${{ github.event.inputs.commit_id }} 59 | path: Fleet-Provisioning-for-AWS-IoT-embedded-sdk 60 | submodules: recursive 61 | - name: Checkout disabled submodules 62 | run: | 63 | cd Fleet-Provisioning-for-AWS-IoT-embedded-sdk 64 | git submodule update --init --checkout --recursive 65 | - name: Create ZIP 66 | run: | 67 | zip -r Fleet-Provisioning-for-AWS-IoT-embedded-sdk-${{ github.event.inputs.version_number }}.zip Fleet-Provisioning-for-AWS-IoT-embedded-sdk -x "*.git*" 68 | ls ./ 69 | - name: Validate created ZIP 70 | run: | 71 | mkdir zip-check 72 | mv Fleet-Provisioning-for-AWS-IoT-embedded-sdk-${{ github.event.inputs.version_number }}.zip zip-check 73 | cd zip-check 74 | unzip Fleet-Provisioning-for-AWS-IoT-embedded-sdk-${{ github.event.inputs.version_number }}.zip -d Fleet-Provisioning-for-AWS-IoT-embedded-sdk-${{ github.event.inputs.version_number }} 75 | ls Fleet-Provisioning-for-AWS-IoT-embedded-sdk-${{ github.event.inputs.version_number }} 76 | diff -r -x "*.git*" Fleet-Provisioning-for-AWS-IoT-embedded-sdk-${{ github.event.inputs.version_number }}/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/ ../Fleet-Provisioning-for-AWS-IoT-embedded-sdk/ 77 | cd ../ 78 | - name: Build 79 | run: | 80 | cd zip-check/Fleet-Provisioning-for-AWS-IoT-embedded-sdk-${{ github.event.inputs.version_number }}/Fleet-Provisioning-for-AWS-IoT-embedded-sdk 81 | sudo apt-get install -y lcov 82 | cmake -S test -B build/ \ 83 | -G "Unix Makefiles" \ 84 | -DCMAKE_BUILD_TYPE=Debug \ 85 | -DBUILD_CLONE_SUBMODULES=ON \ 86 | -DCMAKE_C_FLAGS='--coverage -Wall -Wextra -Werror -DNDEBUG' 87 | make -C build/ all 88 | - name: Test 89 | run: | 90 | cd zip-check/Fleet-Provisioning-for-AWS-IoT-embedded-sdk-${{ github.event.inputs.version_number }}/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/build/ 91 | ctest -E system --output-on-failure 92 | cd .. 93 | - name: Create artifact of ZIP 94 | uses: actions/upload-artifact@v4 95 | with: 96 | name: Fleet-Provisioning-for-AWS-IoT-embedded-sdk-${{ github.event.inputs.version_number }}.zip 97 | path: zip-check/Fleet-Provisioning-for-AWS-IoT-embedded-sdk-${{ github.event.inputs.version_number }}.zip 98 | deploy-doxygen: 99 | needs: tag-commit 100 | name: Deploy doxygen documentation 101 | runs-on: ubuntu-latest 102 | steps: 103 | - name: Doxygen generation 104 | uses: FreeRTOS/CI-CD-Github-Actions/doxygen-generation@main 105 | with: 106 | ref: ${{ github.event.inputs.version_number }} 107 | add_release: "true" 108 | create-release: 109 | needs: 110 | - create-zip 111 | - deploy-doxygen 112 | name: Create Release and Upload Release Asset 113 | runs-on: ubuntu-latest 114 | steps: 115 | - name: Create Release 116 | id: create_release 117 | uses: actions/create-release@v1 118 | env: 119 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 120 | with: 121 | tag_name: ${{ github.event.inputs.version_number }} 122 | release_name: ${{ github.event.inputs.version_number }} 123 | body: Release ${{ github.event.inputs.version_number }} of AWS IoT Fleet Provisioning. 124 | draft: false 125 | prerelease: false 126 | - name: Download ZIP artifact 127 | uses: actions/download-artifact@v4 128 | with: 129 | name: Fleet-Provisioning-for-AWS-IoT-embedded-sdk-${{ github.event.inputs.version_number }}.zip 130 | - name: Upload Release Asset 131 | id: upload-release-asset 132 | uses: actions/upload-release-asset@v1 133 | env: 134 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 135 | with: 136 | upload_url: ${{ steps.create_release.outputs.upload_url }} 137 | asset_path: ./Fleet-Provisioning-for-AWS-IoT-embedded-sdk-${{ github.event.inputs.version_number }}.zip 138 | asset_name: Fleet-Provisioning-for-AWS-IoT-embedded-sdk-${{ github.event.inputs.version_number }}.zip 139 | asset_content_type: application/zip 140 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore documentation output. 2 | **/docs/**/output/* 3 | 4 | # Ignore CMake build directory. 5 | build/ 6 | 7 | # Ignore build artifacts. 8 | *.o 9 | 10 | # Ignore code coverage artifacts. 11 | *.gcda 12 | *.gcno 13 | *.gcov 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/unit-test/Unity"] 2 | path = test/unit-test/Unity 3 | url = https://github.com/ThrowTheSwitch/Unity 4 | update = none 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for AWS IoT Fleet Provisioning Library 2 | 3 | ## v1.2.1 (June 2024) 4 | 5 | ### Other 6 | - Fix doxygen deployment on Github. 7 | 8 | ## v1.2.0 (May 2024) 9 | 10 | ### Other 11 | - [#41](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/pull/41) Fix warnings with ARM GCC compiler when compiled with -Wextra 12 | - [#40](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/pull/40) Add CBMC proof check action in Github CI 13 | - [#39](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/pull/39) MISRA C:2012 compliance checked with Coverity static analysis version 2023.6.1 14 | - [#34](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/pull/34) Update doxygen version to 1.9.6 15 | 16 | ## v1.1.0 (October 2022) 17 | 18 | ### Updates 19 | - [#26](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/pull/26) MISRA C:2012 Compliance Update 20 | - [#25](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/pull/25) Update CBMC starter kit 21 | 22 | ## v1.0.1 (December 2021) 23 | 24 | ### Updates 25 | - [#20](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/pull/20) Update Doxygen version to 1.9.2 26 | 27 | ## v1.0.0 (August 2021) 28 | 29 | This is the first release of the AWS IoT Fleet Provisioning Library. 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /MISRA.md: -------------------------------------------------------------------------------- 1 | # MISRA Compliance 2 | 3 | The Fleet Provisioning Library files conform to the [MISRA C:2012](https://www.misra.org.uk) 4 | guidelines, with some noted exceptions. Compliance is checked with Coverity static analysis. 5 | The specific deviations, suppressed inline, are listed below. 6 | 7 | Additionally, [MISRA configuration file](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/blob/main/tools/coverity/misra.config) contains the project wide deviations. 8 | 9 | ### Suppressed with Coverity Comments 10 | To find the violation references in the source files run grep on the source code 11 | with ( Assuming rule 11.4 violation; with justification in point 2 ): 12 | ``` 13 | grep 'MISRA Ref 11.4.2' . -rI 14 | ``` 15 | 16 | *None.* 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS IoT Fleet Provisioning Library 2 | 3 | **[API Documentation Pages for current and previous releases of this library can be found here](https://aws.github.io/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/)** 4 | 5 | The Fleet Provisioning library enables you to provision IoT devices without 6 | device certificates using the [Fleet Provisioning feature of AWS IoT Core][a1]. 7 | For an overview of provisioning options available, see [Device 8 | provisioning][a2]. This library has no dependencies on any additional libraries 9 | other than the standard C library, and therefore, can be used with any MQTT 10 | library. This library is distributed under the [MIT Open Source License][a3]. 11 | 12 | [a1]: 13 | https://docs.aws.amazon.com/iot/latest/developerguide/provision-wo-cert.html 14 | [a2]: https://docs.aws.amazon.com/iot/latest/developerguide/iot-provision.html 15 | [a3]: LICENSE 16 | 17 | This library has gone through code quality checks including verification that no 18 | function has a [GNU Complexity][a4] score over 8, and checks against deviations 19 | from mandatory rules in the [MISRA coding standard][a5]. Deviations from the 20 | MISRA C:2012 guidelines are documented under [MISRA Deviations][a6]. This 21 | library has also undergone static code analysis using [Coverity static 22 | analysis][a7], and validation of memory safety through the [CBMC automated 23 | reasoning tool][a8]. 24 | 25 | [a4]: https://www.gnu.org/software/complexity/manual/complexity.html 26 | [a5]: https://www.misra.org.uk 27 | [a6]: MISRA.md 28 | [a7]: https://scan.coverity.com/ 29 | [a8]: https://www.cprover.org/cbmc/ 30 | 31 | See memory requirements for this library [here][a9]. 32 | 33 | [a9]: ./docs/doxygen/include/size_table.md 34 | 35 | **AWS IoT Fleet Provisioning Library v1.2.1 36 | [source code](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/tree/v1.2.1/source) 37 | is part of the 38 | [FreeRTOS 202406.00 LTS](https://github.com/FreeRTOS/FreeRTOS-LTS/tree/202406.00-LTS) 39 | release.** 40 | 41 | ## AWS IoT Fleet Provisioning Library Config File 42 | 43 | The AWS IoT Fleet Provisioning Library exposes build configuration macros that 44 | are required for building the library. A list of all the configurations and 45 | their default values are defined in [fleet_provisioning_config_defaults.h][b1]. 46 | To provide custom values for the configuration macros, a config file named 47 | `fleet_provisioning_config.h` can be provided by the application to the library. 48 | 49 | [b1]: source/include/fleet_provisioning_config_defaults.h 50 | 51 | By default, a `fleet_provisioning_config.h` config file is required to build the 52 | library. To disable this requirement and build the library with default 53 | configuration values, provide `FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG` as a 54 | compile time preprocessor macro. 55 | 56 | **Thus, the Fleet Provisioning library can be built by either**: 57 | 58 | - Defining a `fleet_provisioning_config.h` file in the application, and adding 59 | it to the include directories list of the library. 60 | 61 | **OR** 62 | 63 | - Defining the `FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG` preprocessor macro 64 | for the library build. 65 | 66 | ## Building the Library 67 | 68 | The [fleetprovisioningFilePaths.cmake][c1] file contains the information of all 69 | source files and the header include paths required to build the Fleet 70 | Provisioning library. 71 | 72 | [c1]: fleetprovisioningFilePaths.cmake 73 | 74 | As mentioned in the previous section, either a custom config file (i.e. 75 | `fleet_provisioning_config.h`) or `FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG` 76 | macro needs to be provided to build the Fleet Provisioning library. 77 | 78 | For a CMake example of building the Fleet Provisioning library with the 79 | `fleetprovisioningFilePaths.cmake` file, refer to the `coverity_analysis` 80 | library target in [test/CMakeLists.txt][c2] file. 81 | 82 | [c2]: test/CMakeLists.txt 83 | 84 | ## Building Unit Tests 85 | 86 | ### Platform Prerequisites 87 | 88 | - For running unit tests: 89 | - **C90 compiler** like gcc. 90 | - **CMake 3.13.0 or later**. 91 | - For running the coverage target, **gcov** and **lcov** are additionally 92 | required. 93 | 94 | ### Steps to build **Unit Tests** 95 | 96 | 1. Go to the root directory of this repository. 97 | 98 | 1. Run the _cmake_ command: 99 | `cmake -S test -B build -DBUILD_CLONE_SUBMODULES=ON`. 100 | 101 | 1. Run this command to build the library and unit tests: `make -C build all`. 102 | 103 | 1. The generated test executables will be present in `build/bin/tests` folder. 104 | 105 | 1. Run `cd build && ctest` to execute all tests and view the test run summary. 106 | 107 | ## CBMC 108 | 109 | To learn more about CBMC and proofs specifically, review the training material 110 | [here](https://model-checking.github.io/cbmc-training). 111 | 112 | The `test/cbmc/proofs` directory contains CBMC proofs. 113 | 114 | In order to run these proofs you will need to install CBMC and other tools by 115 | following the instructions 116 | [here](https://model-checking.github.io/cbmc-training/installation.html). 117 | 118 | ## Reference examples 119 | 120 | The [AWS IoT Embedded C-SDK repository][e1] contains a demo showing the use of 121 | the AWS IoT Fleet Provisioning Library on a POSIX platform [here][e2]. 122 | 123 | [e1]: https://github.com/aws/aws-iot-device-sdk-embedded-C 124 | [e2]: 125 | https://github.com/aws/aws-iot-device-sdk-embedded-C/tree/main/demos/fleet_provisioning/fleet_provisioning_with_csr 126 | 127 | ## Generating documentation 128 | 129 | The Doxygen references were created using Doxygen version 1.9.6. To generate the 130 | Doxygen pages, please run the following command from the root of this 131 | repository: 132 | 133 | ```sh 134 | doxygen docs/doxygen/config.doxyfile 135 | ``` 136 | 137 | ## Contributing 138 | 139 | See [CONTRIBUTING.md][g1] for information on contributing. 140 | 141 | [g1]: .github/CONTRIBUTING.md 142 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Reporting a Vulnerability 2 | 3 | If you discover a potential security issue in this project, we ask that you notify AWS/Amazon Security 4 | via our [vulnerability reporting page](https://aws.amazon.com/security/vulnerability-reporting/) or directly via email to aws-security@amazon.com. 5 | Please do **not** create a public Github issue. 6 | -------------------------------------------------------------------------------- /cspell.config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | $schema: https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json 3 | version: '0.2' 4 | # Allows things like stringLength 5 | allowCompoundWords: true 6 | 7 | # Read files not to spell check from the git ignore 8 | useGitignore: true 9 | 10 | # Language settings for C 11 | languageSettings: 12 | - caseSensitive: false 13 | enabled: true 14 | languageId: c 15 | locale: "*" 16 | 17 | # Add a dictionary, and the path to the word list 18 | dictionaryDefinitions: 19 | - name: freertos-words 20 | path: '.github/.cSpellWords.txt' 21 | addWords: true 22 | 23 | dictionaries: 24 | - freertos-words 25 | 26 | # Paths and files to ignore 27 | ignorePaths: 28 | - 'dependency' 29 | - 'docs' 30 | - 'ThirdParty' 31 | - 'History.txt' 32 | -------------------------------------------------------------------------------- /docs/doxygen/include/size_table.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
Code Size of AWS IoT Fleet Provisioning (example generated with GCC for ARM Cortex-M)
File
With -O1 Optimization
With -Os Optimization
fleet_provisioning.c
1.0K
0.9K
Total estimates
1.0K
0.9K
21 | -------------------------------------------------------------------------------- /docs/doxygen/layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /docs/doxygen/pages.dox: -------------------------------------------------------------------------------- 1 | /** 2 | @mainpage Overview 3 | @anchor fleet_provisioning 4 | @brief AWS IoT Fleet Provisioning Library 5 | 6 | > By using AWS IoT fleet provisioning, AWS IoT can generate and securely deliver device certificates and private keys to your devices when they connect to AWS IoT for the first time. AWS IoT provides client certificates that are signed by the Amazon Root certificate authority (CA). 7 | Description of Fleet Provisioning from AWS IoT documentation
8 | 9 | For an overview of device provisioning options available with AWS IoT, see 10 | [Device Provisioning](https://docs.aws.amazon.com/iot/latest/developerguide/provision-wo-cert.html). 11 | 12 | AWS IoT Fleet Provisioning allows you to provision devices without 13 | pre-installed unique client certificates. There are two ways to use Fleet 14 | Provisioning: by claim, or by trusted user. If provisioning by claim, devices 15 | used a provisioning claim certificate and private key registered with AWS IoT 16 | to obtain unique device certificates. If provisioning by trusted user, a 17 | trusted user, such as an end user or installation technician, uses a mobile 18 | app to configure the device in its deployed location. 19 | 20 | There are two options for obtaining unique client certificates with AWS IoT 21 | Fleet Provisioning: CreateCertificateFromCsr and CreateKeysAndCertificate. 22 | CreateCertificateFromCsr allows the device to obtain a certificate by providing 23 | a certificate signing request, keeping the private key secure on the device. 24 | CreateKeysAndCertificate provides a new certificate and corresponding private 25 | key. 26 | 27 | @section fleet_provisioning_memory_requirements Memory Requirements 28 | @brief Memory requirements of the AWS IoT Fleet Provisioning Library. 29 | 30 | @include{doc} size_table.md 31 | */ 32 | 33 | /** 34 | @page fleet_provisioning_design Design 35 | AWS IoT Fleet Provisioning Library Design 36 | 37 | The AWS IoT Fleet Provisioning library provides macros and functions to 38 | assemble and parse MQTT topic strings reserved for the Fleet Provisioning 39 | feature of AWS IoT core. Applications can use this library in conjunction with 40 | any MQTT library to interact with the AWS IoT Fleet Provisioning APIs. 41 | 42 | The diagram below demonstrates the happy path an application can take to use 43 | the Fleet Provisioning library, a MQTT library, and a JSON or CBOR library to 44 | interact with the AWS IoT Fleet Provisioning APIs. 45 | 46 | \image html fleet_provisioning_operations.png "Fleet Provisioning Library example operation diagram" width=90% 47 | */ 48 | 49 | /** 50 | @page fleet_provisioning_config Configurations 51 | @brief Configurations of the AWS IoT Fleet Provisioning Library. 52 | 54 | @par configpagestyle 55 | 56 | Configuration settings are C pre-processor constants. They can be set with a 57 | `\#define` in the config file (`fleet_provisioning_config.h`) or by using a 58 | compiler option such as -D in gcc. 59 | 60 | @section FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG 61 | @copydoc FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG 62 | 63 | @section fleet_provisioning_logerror LogError 64 | @copydoc LogError 65 | 66 | @section fleet_provisioning_logwarn LogWarn 67 | @copydoc LogWarn 68 | 69 | @section fleet_provisioning_loginfo LogInfo 70 | @copydoc LogInfo 71 | 72 | @section fleet_provisioning_logdebug LogDebug 73 | @copydoc LogDebug 74 | */ 75 | 76 | /** 77 | @page fleet_provisioning_functions Functions 78 | @brief Primary functions of the AWS IoT Fleet Provisioning Library:

79 | @subpage fleet_provisioning_getregisterthingtopic_function
80 | @subpage fleet_provisioning_matchtopic_function
81 | 82 | @page fleet_provisioning_getregisterthingtopic_function FleetProvisioning_GetRegisterThingTopic 83 | @snippet fleet_provisioning.h declare_fleet_provisioning_getregisterthingtopic 84 | @copydoc FleetProvisioning_GetRegisterThingTopic 85 | 86 | @page fleet_provisioning_matchtopic_function FleetProvisioning_MatchTopic 87 | @snippet fleet_provisioning.h declare_fleet_provisioning_matchtopic 88 | @copydoc FleetProvisioning_MatchTopic 89 | */ 90 | 91 | 93 | /** 94 | @defgroup fleet_provisioning_enum_types Enumerated Types 95 | @brief Enumerated types of the AWS IoT Fleet Provisioning Library 96 | */ 97 | 98 | /** 99 | @defgroup fleet_provisioning_constants Constants 100 | @brief Constants defined in the AWS IoT Fleet Provisioning Library 101 | */ 102 | -------------------------------------------------------------------------------- /docs/doxygen/porting.dox: -------------------------------------------------------------------------------- 1 | /** 2 | @page fleet_provisioning_porting Porting Guide 3 | @brief Guide for porting the AWS IoT Fleet Provisioning Library to a new 4 | platform. 5 | 6 | @section fleet_provisioning_porting_config Configuration Macros 7 | @brief Configuration macros that can be set in the config header 8 | `fleet_provisioning_config.h`, or passed in as compiler options. 9 | 10 | The following optional logging macros are used throughout the library: 11 | - @ref LogError 12 | - @ref LogWarn 13 | - @ref LogInfo 14 | - @ref LogDebug 15 | 16 | @see [Configurations](@ref fleet_provisioning_config) for more information. 17 | 18 | @note Regardless of whether the above macros are defined in 19 | `fleet_provisioning_config.h` or passed as compiler options, by default the 20 | `fleet_provisioning_config.h` file is needed to build the AWS IoT Fleet 21 | Provisioning Library. To disable this requirement and build the library with 22 | default configuration values, provide 23 | `FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG` as a compile time preprocessor 24 | macro. 25 | */ 26 | -------------------------------------------------------------------------------- /docs/doxygen/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Stylesheet for Doxygen HTML output. 3 | * 4 | * This file defines styles for custom elements in the header/footer and 5 | * overrides some of the default Doxygen styles. 6 | * 7 | * Styles in this file do not affect the treeview sidebar. 8 | */ 9 | 10 | /* Set the margins to place a small amount of whitespace on the left and right 11 | * side of the page. */ 12 | div.contents { 13 | margin-left:4em; 14 | margin-right:4em; 15 | } 16 | 17 | /* Justify text in paragraphs. */ 18 | p { 19 | text-align: justify; 20 | } 21 | 22 | /* Style of section headings. */ 23 | h1 { 24 | border-bottom: 1px solid #879ECB; 25 | color: #354C7B; 26 | font-size: 160%; 27 | font-weight: normal; 28 | padding-bottom: 4px; 29 | padding-top: 8px; 30 | } 31 | 32 | /* Style of subsection headings. */ 33 | h2:not(.memtitle):not(.groupheader) { 34 | font-size: 125%; 35 | margin-bottom: 0px; 36 | margin-top: 16px; 37 | padding: 0px; 38 | } 39 | 40 | /* Style of paragraphs immediately after subsection headings. */ 41 | h2 + p { 42 | margin: 0px; 43 | padding: 0px; 44 | } 45 | 46 | /* Style of subsection headings. */ 47 | h3 { 48 | font-size: 100%; 49 | margin-bottom: 0px; 50 | margin-left: 2em; 51 | margin-right: 2em; 52 | } 53 | 54 | /* Style of paragraphs immediately after subsubsection headings. */ 55 | h3 + p { 56 | margin-top: 0px; 57 | margin-left: 2em; 58 | margin-right: 2em; 59 | } 60 | 61 | /* Style of the prefix "AWS IoT Device SDK C" that appears in the header. */ 62 | #csdkprefix { 63 | color: #757575; 64 | } 65 | 66 | /* Style of the "Return to main page" link that appears in the header. */ 67 | #returntomain { 68 | padding: 0.5em; 69 | } 70 | 71 | /* Style of the dividers on Configuration Settings pages. */ 72 | div.configpagedivider { 73 | margin-left: 0px !important; 74 | margin-right: 0px !important; 75 | margin-top: 20px !important; 76 | } 77 | 78 | /* Style of configuration setting names. */ 79 | dl.section.user ~ h1 { 80 | border-bottom: none; 81 | color: #000000; 82 | font-family: monospace, fixed; 83 | font-size: 16px; 84 | margin-bottom: 0px; 85 | margin-left: 2em; 86 | margin-top: 1.5em; 87 | } 88 | 89 | /* Style of paragraphs on a configuration settings page. */ 90 | dl.section.user ~ * { 91 | margin-bottom: 10px; 92 | margin-left: 4em; 93 | margin-right: 4em; 94 | margin-top: 0px; 95 | } 96 | 97 | /* Hide the configuration setting marker. */ 98 | dl.section.user { 99 | display: none; 100 | } 101 | 102 | /* Overrides for code fragments and lines. */ 103 | div.fragment { 104 | background: #ffffff; 105 | border: none; 106 | padding: 5px; 107 | } 108 | 109 | div.line { 110 | color: #3a3a3a; 111 | } 112 | 113 | /* Overrides for code syntax highlighting colors. */ 114 | span.comment { 115 | color: #008000; 116 | } 117 | 118 | span.keyword, span.keywordtype, span.keywordflow { 119 | color: #0000ff; 120 | } 121 | 122 | span.preprocessor { 123 | color: #50015a; 124 | } 125 | 126 | span.stringliteral, span.charliteral { 127 | color: #800c0c; 128 | } 129 | 130 | a.code, a.code:visited, a.line, a.line:visited { 131 | color: #496194; 132 | } 133 | 134 | div.image img[src="fleet_provisioning_operations.png"] { 135 | max-width: 1000px; 136 | } 137 | -------------------------------------------------------------------------------- /docs/plantuml/fleet_provisioning_operations.pu: -------------------------------------------------------------------------------- 1 | @startuml 2 | skinparam dpi 300 3 | skinparam classFontSize 8 4 | skinparam classFontName Helvetica 5 | autonumber 6 | 7 | participant "User Application" as App 8 | participant "Fleet Provisioning Library" as FleetProv 9 | participant "MQTT Client" as MQTT 10 | participant "JSON/CBOR Library" as JSON 11 | 12 | activate App 13 | 14 | App -> MQTT : Connect to AWS IoT broker with claim credentials 15 | 16 | activate MQTT 17 | MQTT -> App : Connected 18 | deactivate MQTT 19 | 20 | opt If using CreateCertificateFromCsr 21 | App -> App : Securely generate keys and corresponding CSR 22 | 23 | App -> JSON : Generate CreateCertificateFromCsr request payload 24 | 25 | activate JSON 26 | JSON -> App : Request payload 27 | deactivate JSON 28 | 29 | App -> FleetProv : Get Accepted and Rejected Topic Strings\n for CreateCertificateFromCsr 30 | 31 | activate FleetProv 32 | FleetProv -> App : Accepted and Rejected Topic Strings 33 | deactivate FleetProv 34 | 35 | App -> MQTT : Subscribe to CreateCertificateFromCsr Accepted and Rejected Topics 36 | 37 | activate MQTT 38 | MQTT -> App : Subscription Successful 39 | deactivate MQTT 40 | 41 | App -> FleetProv : Get CreateCertificateFromCsr Publish Topic String 42 | 43 | activate FleetProv 44 | FleetProv -> App : Publish Topic String 45 | deactivate FleetProv 46 | 47 | App -> MQTT : Publish Request 48 | 49 | activate MQTT 50 | MQTT -> App : Published 51 | MQTT -> App : Incoming Publish Message Received 52 | deactivate MQTT 53 | 54 | App -> FleetProv : Is this a Fleet Provisioning CreateCertificateFromCsr\naccepted Message? 55 | 56 | activate FleetProv 57 | FleetProv -> App : Yes 58 | deactivate FleetProv 59 | 60 | App -> JSON : Parse response payload 61 | 62 | activate JSON 63 | JSON -> App : Certificate, certificate id, certificate ownership token 64 | deactivate JSON 65 | 66 | App -> App : Securely store certificate 67 | 68 | else If using CreateKeysAndCertificate 69 | 70 | App -> FleetProv : Generate Accepted and Rejected Topic Strings for RegisterThing 71 | 72 | activate FleetProv 73 | FleetProv -> App : Accepted and Rejected Topic Strings 74 | deactivate FleetProv 75 | 76 | App -> MQTT : Subscribe to CreateKeysAndCertificate Accepted and Rejected Topics 77 | 78 | activate MQTT 79 | MQTT -> App : Subscription Successful 80 | deactivate MQTT 81 | 82 | App -> FleetProv : Get CreateKeysAndCertificate Publish Topic String 83 | 84 | activate FleetProv 85 | FleetProv -> App : Publish Topic String 86 | deactivate FleetProv 87 | 88 | App -> MQTT : Publish Request 89 | 90 | activate MQTT 91 | MQTT -> App : Published 92 | MQTT -> App : Incoming Publish Message Received 93 | deactivate MQTT 94 | 95 | App -> FleetProv : Is this a Fleet Provisioning CreateKeysAndCertificate\naccepted Message? 96 | 97 | activate FleetProv 98 | FleetProv -> App : Yes 99 | deactivate FleetProv 100 | 101 | App -> JSON : Parse response payload 102 | 103 | activate JSON 104 | JSON -> App : Private key, certificate, certificate id, certificate ownership token 105 | deactivate JSON 106 | 107 | App -> App : Securely store key and certificate 108 | 109 | end 110 | 111 | App -> JSON : Generate RegisterThing request payload 112 | 113 | activate JSON 114 | JSON -> App : Request payload 115 | deactivate JSON 116 | App -> FleetProv : Generate Accepted and Rejected Topic Strings for RegisterThing 117 | 118 | activate FleetProv 119 | FleetProv -> App : Accepted and Rejected Topic Strings 120 | deactivate FleetProv 121 | 122 | App -> MQTT : Subscribe to RegisterThing Accepted and Rejected Topics 123 | 124 | activate MQTT 125 | MQTT -> App : Subscription Successful 126 | deactivate MQTT 127 | 128 | App -> FleetProv : Get RegisterThing Publish Topic String 129 | 130 | activate FleetProv 131 | FleetProv -> App : Publish Topic String 132 | deactivate FleetProv 133 | 134 | App -> MQTT : Publish Request 135 | 136 | activate MQTT 137 | MQTT -> App : Published 138 | MQTT -> App : Incoming Publish Message Received 139 | deactivate MQTT 140 | 141 | App -> FleetProv : Is this a Fleet Provisioning RegisterThing accepted Message? 142 | 143 | activate FleetProv 144 | FleetProv -> App : Yes 145 | deactivate FleetProv 146 | 147 | App -> JSON : Parse response payload 148 | 149 | activate JSON 150 | JSON -> App : Thing name, device configuration 151 | deactivate JSON 152 | 153 | App -> MQTT : Disconnect from AWS IoT broker 154 | 155 | activate MQTT 156 | MQTT -> App : Disconnected 157 | deactivate MQTT 158 | 159 | App -> MQTT : Connect to AWS IoT broker with newly provisioned credentials 160 | 161 | activate MQTT 162 | MQTT -> App : Connected 163 | deactivate MQTT 164 | deactivate App 165 | @enduml 166 | -------------------------------------------------------------------------------- /docs/plantuml/images/fleet_provisioning_operations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/629ec2a21d91ade13bfb995aace2223b36c4cd2e/docs/plantuml/images/fleet_provisioning_operations.png -------------------------------------------------------------------------------- /fleetprovisioningFilePaths.cmake: -------------------------------------------------------------------------------- 1 | # This file is to add source files and include directories 2 | # into variables so that it can be reused from different repositories 3 | # in their Cmake based build system by including this file. 4 | # 5 | # Files specific to the repository such as test runner, platform tests 6 | # are not added to the variables. 7 | 8 | # Fleet Provisioning library source files. 9 | set( FLEET_PROVISIONING_SOURCES 10 | "${CMAKE_CURRENT_LIST_DIR}/source/fleet_provisioning.c" ) 11 | 12 | # Fleet Provisioning library public include directories. 13 | set( FLEET_PROVISIONING_INCLUDE_PUBLIC_DIRS 14 | "${CMAKE_CURRENT_LIST_DIR}/source/include" ) 15 | -------------------------------------------------------------------------------- /manifest.yml: -------------------------------------------------------------------------------- 1 | name: "Fleet-Provisioning-for-AWS-IoT-embedded-sdk" 2 | version: "v1.2.1" 3 | description: | 4 | "Library for using the Fleet Provisioning feature of AWS IoT Core on embedded devices.\n" 5 | license: "MIT" 6 | -------------------------------------------------------------------------------- /source/fleet_provisioning.c: -------------------------------------------------------------------------------- 1 | /* 2 | * AWS IoT Fleet Provisioning v1.2.1 3 | * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | * this software and associated documentation files (the "Software"), to deal in 9 | * the Software without restriction, including without limitation the rights to 10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | * the Software, and to permit persons to whom the Software is furnished to do so, 12 | * subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @file fleet_provisioning.c 27 | * @brief Implementation of the AWS IoT Fleet Provisioning Library. 28 | */ 29 | 30 | /* Standard includes. */ 31 | #include 32 | #include 33 | #include 34 | 35 | /* Fleet Provisioning API include. */ 36 | #include "fleet_provisioning.h" 37 | 38 | /** 39 | * @brief Identifier for which of the topic suffixes for a given format and 40 | * Fleet Provisioning MQTT API. 41 | */ 42 | typedef enum 43 | { 44 | TopicPublish, 45 | TopicAccepted, 46 | TopicRejected, 47 | TopicInvalidSuffix 48 | } TopicSuffix_t; 49 | 50 | /** 51 | * @brief Identifier for which of the topics in each Fleet Provisioning MQTT API. 52 | */ 53 | typedef enum 54 | { 55 | TopicJsonPublish, 56 | TopicJsonAccepted, 57 | TopicJsonRejected, 58 | TopicCborPublish, 59 | TopicCborAccepted, 60 | TopicCborRejected, 61 | TopicInvalidFormatSuffix 62 | } TopicFormatSuffix_t; 63 | 64 | /** 65 | * @brief Get the topic length for a given RegisterThing topic. 66 | * 67 | * @param[in] templateNameLength the length of the template name registered with 68 | * AWS IoT. 69 | * @param[in] format The RegisterThing API format to use. 70 | * @param[in] topic The RegisterThing API format to use. 71 | * 72 | * @return the template length for the given RegisterThing topic. 73 | */ 74 | static uint16_t getRegisterThingTopicLength( uint16_t templateNameLength, 75 | FleetProvisioningFormat_t format, 76 | FleetProvisioningApiTopics_t topic ); 77 | 78 | /** 79 | * @brief Write the given piece of the topic to the remaining buffer and advance 80 | * the remaining buffer pointer. 81 | * 82 | * The caller is responsible for assuring that there is enough space remaining 83 | * in the buffer to write the given string. 84 | * 85 | * @param[in,out] pBufferCursor Pointer to the remaining buffer. 86 | * @param[in] fragment The piece of the topic string to write. 87 | * @param[in] length The length of @p fragment. 88 | */ 89 | static void writeTopicFragmentAndAdvance( char ** pBufferCursor, 90 | const char * fragment, 91 | uint16_t length ); 92 | 93 | /** 94 | * @brief Check the parameters for FleetProvisioning_GetRegisterThingTopic(). 95 | * 96 | * @param[in] pTopicBuffer The buffer to write the topic string into. 97 | * @param[in] format The desired RegisterThing format. 98 | * @param[in] topic The desired RegisterThing topic. 99 | * @param[in] pTemplateName The name of the provisioning template configured 100 | * with AWS IoT. 101 | * @param[in] templateNameLength The length of @p pTemplateName. 102 | * @param[in] pOutLength The length of the topic string written to 103 | * the buffer. 104 | * 105 | * @return FleetProvisioningSuccess if no errors are found with the parameters; 106 | * FleetProvisioningBadParameter otherwise. 107 | */ 108 | static FleetProvisioningStatus_t GetRegisterThingTopicCheckParams( const char * pTopicBuffer, 109 | FleetProvisioningFormat_t format, 110 | FleetProvisioningApiTopics_t topic, 111 | const char * pTemplateName, 112 | uint16_t templateNameLength, 113 | const uint16_t * pOutLength ); 114 | 115 | /** 116 | * @brief Match the suffix from the remaining topic string and return the 117 | * corresponding suffix. 118 | * 119 | * Suffix: empty, /accepted, or /rejected. 120 | * 121 | * @param[in] pRemainingTopic The remaining portion of the topic. 122 | * @param[in] remainingLength The remaining length of the topic. 123 | * 124 | * @return The matching #TopicSuffix_t. 125 | */ 126 | static TopicSuffix_t parseTopicSuffix( const char * pRemainingTopic, 127 | uint16_t remainingLength ); 128 | 129 | /** 130 | * @brief Match the format and suffix from the remaining topic string and 131 | * return the corresponding format and suffix. 132 | * 133 | * Format: json or cbor. 134 | * Suffix: empty, /accepted, or /rejected. 135 | * 136 | * @param[in] pRemainingTopic The remaining portion of the topic. 137 | * @param[in] remainingLength The remaining length of the topic. 138 | * 139 | * @return The matching #TopicFormatSuffix_t. 140 | */ 141 | static TopicFormatSuffix_t parseTopicFormatSuffix( const char * pRemainingTopic, 142 | uint16_t remainingLength ); 143 | 144 | /** 145 | * @brief Match a topic string with the CreateCertificateFromCsr topics. 146 | * 147 | * @param[in] pTopic The topic string to match. 148 | * @param[in] topicLength The length of the topic string. 149 | * 150 | * @return The matching #FleetProvisioningTopic_t if the topic string is a 151 | * Fleet Provisioning CreateCertificateFromCsr topic, else 152 | * FleetProvisioningInvalidTopic. 153 | */ 154 | static FleetProvisioningTopic_t parseCreateCertificateFromCsrTopic( const char * pTopic, 155 | uint16_t topicLength ); 156 | 157 | /** 158 | * @brief Match a topic string with the CreateKeysAndCertificate topics. 159 | * 160 | * @param[in] pTopic The topic string to match. 161 | * @param[in] topicLength The length of the topic string. 162 | * 163 | * @return The matching FleetProvisioningTopic_t if the topic string is a 164 | * Fleet Provisioning CreateKeysAndCertificate topic, else 165 | * FleetProvisioningInvalidTopic. 166 | */ 167 | static FleetProvisioningTopic_t parseCreateKeysAndCertificateTopic( const char * pTopic, 168 | uint16_t topicLength ); 169 | 170 | /** 171 | * @brief Match a topic string with the RegisterThing topics. 172 | * 173 | * @param[in] pTopic The topic string to match. 174 | * @param[in] topicLength The length of the topic string. 175 | * 176 | * @return The matching #FleetProvisioningTopic_t if the topic string is a 177 | * Fleet Provisioning RegisterThing topic, else 178 | * FleetProvisioningInvalidTopic. 179 | */ 180 | static FleetProvisioningTopic_t parseRegisterThingTopic( const char * pTopic, 181 | uint16_t topicLength ); 182 | 183 | /** 184 | * @brief Check if the remaining buffer starts with a specified string. If so, 185 | * moves the remaining buffer pointer past the matched section and updates the 186 | * remaining length. 187 | * 188 | * @param[in,out] pBufferCursor Pointer to the remaining portion of the buffer. 189 | * @param[in,out] pRemainingLength The remaining length of the buffer. 190 | * @param[in] matchString The string to match against. 191 | * @param[in] matchLength The length of @p matchString. 192 | * 193 | * @return FleetProvisioningSuccess if the string matches and is skipped over; 194 | * FleetProvisioningNoMatch otherwise. 195 | */ 196 | static FleetProvisioningStatus_t consumeIfMatch( const char ** pBufferCursor, 197 | uint16_t * pRemainingLength, 198 | const char * matchString, 199 | uint16_t matchLength ); 200 | 201 | /** 202 | * @brief Move the remaining topic pointer past the template name in the 203 | * unparsed topic so far, and update the remaining topic length. 204 | * 205 | * The end of thing name is marked by a forward slash. A zero length thing name 206 | * is not valid. 207 | * 208 | * This function extracts the same template name from the following topic strings: 209 | * - $aws/provisioning-templates/TEMPLATE_NAME/provision/json/accepted 210 | * - $aws/provisioning-templates/TEMPLATE_NAME 211 | * The second topic is not a valid Fleet Provisioning topic and the matching 212 | * will fail when we try to match the bridge part. 213 | * 214 | * @param[in,out] pTopicCursor Pointer to the remaining topic string. 215 | * @param[in,out] pRemainingLength Pointer to the length of the remaining topic string. 216 | * 217 | * @return FleetProvisioningSuccess if a valid template name is skipped over; 218 | * FleetProvisioningNoMatch otherwise. 219 | */ 220 | static FleetProvisioningStatus_t consumeTemplateName( const char ** pTopicCursor, 221 | uint16_t * pRemainingLength ); 222 | /*-----------------------------------------------------------*/ 223 | 224 | static uint16_t getRegisterThingTopicLength( uint16_t templateNameLength, 225 | FleetProvisioningFormat_t format, 226 | FleetProvisioningApiTopics_t topic ) 227 | { 228 | uint16_t topicLength = 0U; 229 | 230 | assert( ( templateNameLength != 0U ) && 231 | ( templateNameLength <= FP_TEMPLATENAME_MAX_LENGTH ) ); 232 | assert( ( format == FleetProvisioningJson ) || ( format == FleetProvisioningCbor ) ); 233 | assert( ( topic >= FleetProvisioningPublish ) && ( topic <= FleetProvisioningRejected ) ); 234 | 235 | topicLength = FP_REGISTER_API_LENGTH_PREFIX + 236 | templateNameLength + 237 | FP_REGISTER_API_LENGTH_BRIDGE; 238 | 239 | if( format == FleetProvisioningJson ) 240 | { 241 | topicLength += FP_API_LENGTH_JSON_FORMAT; 242 | } 243 | 244 | if( format == FleetProvisioningCbor ) 245 | { 246 | topicLength += FP_API_LENGTH_CBOR_FORMAT; 247 | } 248 | 249 | if( topic == FleetProvisioningAccepted ) 250 | { 251 | topicLength += FP_API_LENGTH_ACCEPTED_SUFFIX; 252 | } 253 | 254 | if( topic == FleetProvisioningRejected ) 255 | { 256 | topicLength += FP_API_LENGTH_REJECTED_SUFFIX; 257 | } 258 | 259 | return topicLength; 260 | } 261 | /*-----------------------------------------------------------*/ 262 | 263 | static void writeTopicFragmentAndAdvance( char ** pBufferCursor, 264 | const char * fragment, 265 | uint16_t length ) 266 | { 267 | assert( pBufferCursor != NULL ); 268 | assert( *pBufferCursor != NULL ); 269 | assert( fragment != NULL ); 270 | 271 | ( void ) memcpy( ( void * ) *pBufferCursor, 272 | ( const void * ) fragment, 273 | ( size_t ) length ); 274 | 275 | *pBufferCursor = &( ( *pBufferCursor )[ length ] ); 276 | } 277 | /*-----------------------------------------------------------*/ 278 | 279 | static FleetProvisioningStatus_t GetRegisterThingTopicCheckParams( const char * pTopicBuffer, 280 | FleetProvisioningFormat_t format, 281 | FleetProvisioningApiTopics_t topic, 282 | const char * pTemplateName, 283 | uint16_t templateNameLength, 284 | const uint16_t * pOutLength ) 285 | { 286 | FleetProvisioningStatus_t ret = FleetProvisioningError; 287 | 288 | if( ( pTopicBuffer == NULL ) || 289 | ( ( format != FleetProvisioningJson ) && ( format != FleetProvisioningCbor ) ) || 290 | ( ( topic != FleetProvisioningPublish ) && ( topic != FleetProvisioningAccepted ) && ( topic != FleetProvisioningRejected ) ) || 291 | ( pTemplateName == NULL ) || 292 | ( templateNameLength == 0U ) || 293 | ( templateNameLength > FP_TEMPLATENAME_MAX_LENGTH ) || 294 | ( pOutLength == NULL ) ) 295 | { 296 | ret = FleetProvisioningBadParameter; 297 | 298 | LogError( ( "Invalid input parameter. pTopicBuffer: %p, format: %d, topic: %d," 299 | " pTemplateName: %p, templateNameLength: %u, pOutLength: %p.", 300 | ( const void * ) pTopicBuffer, 301 | ( int ) format, 302 | ( int ) topic, 303 | ( const void * ) pTemplateName, 304 | ( unsigned int ) templateNameLength, 305 | ( const void * ) pOutLength ) ); 306 | } 307 | else 308 | { 309 | ret = FleetProvisioningSuccess; 310 | } 311 | 312 | return ret; 313 | } 314 | /*-----------------------------------------------------------*/ 315 | 316 | static TopicSuffix_t parseTopicSuffix( const char * pRemainingTopic, 317 | uint16_t remainingLength ) 318 | { 319 | TopicSuffix_t ret = TopicInvalidSuffix; 320 | FleetProvisioningStatus_t status = FleetProvisioningNoMatch; 321 | const char * pTopicCursor = pRemainingTopic; 322 | uint16_t cursorLength = remainingLength; 323 | 324 | assert( pRemainingTopic != NULL ); 325 | 326 | /* Check if publish topic */ 327 | if( cursorLength == 0U ) 328 | { 329 | ret = TopicPublish; 330 | status = FleetProvisioningSuccess; 331 | } 332 | 333 | /* Check if accepted topic */ 334 | if( status == FleetProvisioningNoMatch ) 335 | { 336 | status = consumeIfMatch( &pTopicCursor, 337 | &cursorLength, 338 | FP_API_ACCEPTED_SUFFIX, 339 | FP_API_LENGTH_ACCEPTED_SUFFIX ); 340 | 341 | if( status == FleetProvisioningSuccess ) 342 | { 343 | if( cursorLength == 0U ) 344 | { 345 | ret = TopicAccepted; 346 | } 347 | else 348 | { 349 | status = FleetProvisioningError; 350 | } 351 | } 352 | } 353 | 354 | /* Check if rejected topic */ 355 | if( status == FleetProvisioningNoMatch ) 356 | { 357 | status = consumeIfMatch( &pTopicCursor, 358 | &cursorLength, 359 | FP_API_REJECTED_SUFFIX, 360 | FP_API_LENGTH_REJECTED_SUFFIX ); 361 | 362 | if( status == FleetProvisioningSuccess ) 363 | { 364 | if( cursorLength == 0U ) 365 | { 366 | ret = TopicRejected; 367 | } 368 | } 369 | } 370 | 371 | return ret; 372 | } 373 | /*-----------------------------------------------------------*/ 374 | 375 | static TopicFormatSuffix_t parseTopicFormatSuffix( const char * pRemainingTopic, 376 | uint16_t remainingLength ) 377 | { 378 | /* Table of JSON format and suffixes in same order as TopicSuffix_t. */ 379 | static const TopicFormatSuffix_t jsonSuffixes[] = 380 | { 381 | TopicJsonPublish, 382 | TopicJsonAccepted, 383 | TopicJsonRejected, 384 | TopicInvalidFormatSuffix 385 | }; 386 | /* Table of CBOR format and suffixes in same order as TopicSuffix_t. */ 387 | static const TopicFormatSuffix_t cborSuffixes[] = 388 | { 389 | TopicCborPublish, 390 | TopicCborAccepted, 391 | TopicCborRejected, 392 | TopicInvalidFormatSuffix 393 | }; 394 | TopicFormatSuffix_t ret = TopicInvalidFormatSuffix; 395 | TopicSuffix_t suffix = TopicInvalidSuffix; 396 | FleetProvisioningStatus_t status = FleetProvisioningNoMatch; 397 | const char * pTopicCursor = pRemainingTopic; 398 | uint16_t cursorLength = remainingLength; 399 | 400 | assert( pRemainingTopic != NULL ); 401 | 402 | /* Check if JSON format */ 403 | status = consumeIfMatch( &pTopicCursor, 404 | &cursorLength, 405 | FP_API_JSON_FORMAT, 406 | FP_API_LENGTH_JSON_FORMAT ); 407 | 408 | if( status == FleetProvisioningSuccess ) 409 | { 410 | /* Match suffix */ 411 | suffix = parseTopicSuffix( pTopicCursor, cursorLength ); 412 | ret = jsonSuffixes[ suffix ]; 413 | } 414 | 415 | if( status == FleetProvisioningNoMatch ) 416 | { 417 | /* Check if CBOR format */ 418 | status = consumeIfMatch( &pTopicCursor, 419 | &cursorLength, 420 | FP_API_CBOR_FORMAT, 421 | FP_API_LENGTH_CBOR_FORMAT ); 422 | 423 | if( status == FleetProvisioningSuccess ) 424 | { 425 | /* Match suffix */ 426 | suffix = parseTopicSuffix( pTopicCursor, cursorLength ); 427 | ret = cborSuffixes[ suffix ]; 428 | } 429 | } 430 | 431 | return ret; 432 | } 433 | /*-----------------------------------------------------------*/ 434 | 435 | static FleetProvisioningTopic_t parseCreateCertificateFromCsrTopic( const char * pTopic, 436 | uint16_t topicLength ) 437 | { 438 | /* Table of topics in the same order as TopicFormatSuffix_t. */ 439 | static const FleetProvisioningTopic_t createCertificateFromCsrApi[] = 440 | { 441 | FleetProvJsonCreateCertFromCsrPublish, 442 | FleetProvJsonCreateCertFromCsrAccepted, 443 | FleetProvJsonCreateCertFromCsrRejected, 444 | FleetProvCborCreateCertFromCsrPublish, 445 | FleetProvCborCreateCertFromCsrAccepted, 446 | FleetProvCborCreateCertFromCsrRejected, 447 | FleetProvisioningInvalidTopic 448 | }; 449 | FleetProvisioningTopic_t ret = FleetProvisioningInvalidTopic; 450 | FleetProvisioningStatus_t status = FleetProvisioningError; 451 | TopicFormatSuffix_t rest = TopicInvalidFormatSuffix; 452 | const char * pTopicCursor = pTopic; 453 | uint16_t cursorLength = topicLength; 454 | 455 | assert( pTopic != NULL ); 456 | 457 | /* Check if prefix matches */ 458 | status = consumeIfMatch( &pTopicCursor, 459 | &cursorLength, 460 | FP_CREATE_CERT_API_PREFIX, 461 | FP_CREATE_CERT_API_LENGTH_PREFIX ); 462 | 463 | if( status == FleetProvisioningSuccess ) 464 | { 465 | /* Match format and suffix */ 466 | rest = parseTopicFormatSuffix( pTopicCursor, cursorLength ); 467 | ret = createCertificateFromCsrApi[ rest ]; 468 | } 469 | 470 | return ret; 471 | } 472 | /*-----------------------------------------------------------*/ 473 | 474 | static FleetProvisioningTopic_t parseCreateKeysAndCertificateTopic( const char * pTopic, 475 | uint16_t topicLength ) 476 | { 477 | /* Table of topics in the same order as TopicFormatSuffix_t. */ 478 | static const FleetProvisioningTopic_t createKeysAndCertificateApi[] = 479 | { 480 | FleetProvJsonCreateKeysAndCertPublish, 481 | FleetProvJsonCreateKeysAndCertAccepted, 482 | FleetProvJsonCreateKeysAndCertRejected, 483 | FleetProvCborCreateKeysAndCertPublish, 484 | FleetProvCborCreateKeysAndCertAccepted, 485 | FleetProvCborCreateKeysAndCertRejected, 486 | FleetProvisioningInvalidTopic 487 | }; 488 | FleetProvisioningTopic_t ret = FleetProvisioningInvalidTopic; 489 | FleetProvisioningStatus_t status = FleetProvisioningError; 490 | TopicFormatSuffix_t rest = TopicInvalidFormatSuffix; 491 | const char * pTopicCursor = pTopic; 492 | uint16_t cursorLength = topicLength; 493 | 494 | assert( pTopic != NULL ); 495 | 496 | /* Check if prefix matches */ 497 | status = consumeIfMatch( &pTopicCursor, 498 | &cursorLength, 499 | FP_CREATE_KEYS_API_PREFIX, 500 | FP_CREATE_KEYS_API_LENGTH_PREFIX ); 501 | 502 | if( status == FleetProvisioningSuccess ) 503 | { 504 | /* Match format and suffix */ 505 | rest = parseTopicFormatSuffix( pTopicCursor, cursorLength ); 506 | ret = createKeysAndCertificateApi[ rest ]; 507 | } 508 | 509 | return ret; 510 | } 511 | /*-----------------------------------------------------------*/ 512 | 513 | static FleetProvisioningTopic_t parseRegisterThingTopic( const char * pTopic, 514 | uint16_t topicLength ) 515 | { 516 | /* Table of topics in the same order as TopicFormatSuffix_t. */ 517 | static const FleetProvisioningTopic_t registerThingApi[] = 518 | { 519 | FleetProvJsonRegisterThingPublish, 520 | FleetProvJsonRegisterThingAccepted, 521 | FleetProvJsonRegisterThingRejected, 522 | FleetProvCborRegisterThingPublish, 523 | FleetProvCborRegisterThingAccepted, 524 | FleetProvCborRegisterThingRejected, 525 | FleetProvisioningInvalidTopic 526 | }; 527 | FleetProvisioningTopic_t ret = FleetProvisioningInvalidTopic; 528 | FleetProvisioningStatus_t status = FleetProvisioningError; 529 | TopicFormatSuffix_t rest = TopicInvalidFormatSuffix; 530 | const char * pTopicCursor = pTopic; 531 | uint16_t cursorLength = topicLength; 532 | 533 | assert( pTopic != NULL ); 534 | 535 | /* Check if prefix matches */ 536 | status = consumeIfMatch( &pTopicCursor, 537 | &cursorLength, 538 | FP_REGISTER_API_PREFIX, 539 | FP_REGISTER_API_LENGTH_PREFIX ); 540 | 541 | if( status == FleetProvisioningSuccess ) 542 | { 543 | /* Skip template name */ 544 | status = consumeTemplateName( &pTopicCursor, 545 | &cursorLength ); 546 | } 547 | 548 | if( status == FleetProvisioningSuccess ) 549 | { 550 | /* Check if bridge matches */ 551 | status = consumeIfMatch( &pTopicCursor, 552 | &cursorLength, 553 | FP_REGISTER_API_BRIDGE, 554 | FP_REGISTER_API_LENGTH_BRIDGE ); 555 | } 556 | 557 | if( status == FleetProvisioningSuccess ) 558 | { 559 | /* Match format and suffix */ 560 | rest = parseTopicFormatSuffix( pTopicCursor, cursorLength ); 561 | ret = registerThingApi[ rest ]; 562 | } 563 | 564 | return ret; 565 | } 566 | /*-----------------------------------------------------------*/ 567 | 568 | static FleetProvisioningStatus_t consumeIfMatch( const char ** pBufferCursor, 569 | uint16_t * pRemainingLength, 570 | const char * matchString, 571 | uint16_t matchLength ) 572 | { 573 | FleetProvisioningStatus_t status = FleetProvisioningError; 574 | int32_t cmpVal = -1; 575 | 576 | assert( pBufferCursor != NULL ); 577 | assert( *pBufferCursor != NULL ); 578 | assert( pRemainingLength != NULL ); 579 | assert( matchString != NULL ); 580 | 581 | if( *pRemainingLength < matchLength ) 582 | { 583 | status = FleetProvisioningNoMatch; 584 | } 585 | else 586 | { 587 | cmpVal = strncmp( *pBufferCursor, 588 | matchString, 589 | ( size_t ) matchLength ); 590 | 591 | if( cmpVal != 0 ) 592 | { 593 | status = FleetProvisioningNoMatch; 594 | } 595 | else 596 | { 597 | status = FleetProvisioningSuccess; 598 | *pBufferCursor = &( ( *pBufferCursor )[ matchLength ] ); 599 | *pRemainingLength -= matchLength; 600 | } 601 | } 602 | 603 | return status; 604 | } 605 | /*-----------------------------------------------------------*/ 606 | 607 | static FleetProvisioningStatus_t consumeTemplateName( const char ** pTopicCursor, 608 | uint16_t * pRemainingLength ) 609 | { 610 | FleetProvisioningStatus_t ret = FleetProvisioningNoMatch; 611 | uint16_t i = 0U; 612 | 613 | assert( pTopicCursor != NULL ); 614 | assert( *pTopicCursor != NULL ); 615 | assert( pRemainingLength != NULL ); 616 | 617 | /* Find the first forward slash. It marks the end of the template name. */ 618 | for( i = 0U; i < *pRemainingLength; i++ ) 619 | { 620 | if( ( *pTopicCursor )[ i ] == '/' ) 621 | { 622 | break; 623 | } 624 | } 625 | 626 | /* Zero length template name is not valid. */ 627 | if( i > 0U ) 628 | { 629 | ret = FleetProvisioningSuccess; 630 | *pTopicCursor = &( ( *pTopicCursor )[ i ] ); 631 | *pRemainingLength -= i; 632 | } 633 | 634 | return ret; 635 | } 636 | /*-----------------------------------------------------------*/ 637 | 638 | FleetProvisioningStatus_t FleetProvisioning_GetRegisterThingTopic( char * pTopicBuffer, 639 | uint16_t bufferLength, 640 | FleetProvisioningFormat_t format, 641 | FleetProvisioningApiTopics_t topic, 642 | const char * pTemplateName, 643 | uint16_t templateNameLength, 644 | uint16_t * pOutLength ) 645 | { 646 | FleetProvisioningStatus_t status = FleetProvisioningError; 647 | uint16_t topicLength = 0U; 648 | char * pBufferCursor = pTopicBuffer; 649 | 650 | status = GetRegisterThingTopicCheckParams( pTopicBuffer, 651 | format, 652 | topic, 653 | pTemplateName, 654 | templateNameLength, 655 | pOutLength ); 656 | 657 | if( status == FleetProvisioningSuccess ) 658 | { 659 | topicLength = getRegisterThingTopicLength( templateNameLength, format, topic ); 660 | 661 | if( bufferLength < topicLength ) 662 | { 663 | status = FleetProvisioningBufferTooSmall; 664 | 665 | LogError( ( "The buffer is too small to hold the topic string. " 666 | "Provided buffer size: %u, Required buffer size: %u.", 667 | ( unsigned int ) bufferLength, 668 | ( unsigned int ) topicLength ) ); 669 | } 670 | } 671 | 672 | if( status == FleetProvisioningSuccess ) 673 | { 674 | /* At this point, it is certain that we have a large enough buffer to 675 | * write the topic string into. */ 676 | 677 | /* Write prefix first. */ 678 | writeTopicFragmentAndAdvance( &pBufferCursor, 679 | FP_REGISTER_API_PREFIX, 680 | FP_REGISTER_API_LENGTH_PREFIX ); 681 | 682 | /* Write template name next. */ 683 | writeTopicFragmentAndAdvance( &pBufferCursor, 684 | pTemplateName, 685 | templateNameLength ); 686 | 687 | /* Write bridge next. */ 688 | writeTopicFragmentAndAdvance( &pBufferCursor, 689 | FP_REGISTER_API_BRIDGE, 690 | FP_REGISTER_API_LENGTH_BRIDGE ); 691 | 692 | /* Write report format. */ 693 | if( format == FleetProvisioningJson ) 694 | { 695 | writeTopicFragmentAndAdvance( &pBufferCursor, 696 | FP_API_JSON_FORMAT, 697 | FP_API_LENGTH_JSON_FORMAT ); 698 | } 699 | 700 | if( format == FleetProvisioningCbor ) 701 | { 702 | writeTopicFragmentAndAdvance( &pBufferCursor, 703 | FP_API_CBOR_FORMAT, 704 | FP_API_LENGTH_CBOR_FORMAT ); 705 | } 706 | 707 | /* Write report suffix. */ 708 | if( topic == FleetProvisioningAccepted ) 709 | { 710 | writeTopicFragmentAndAdvance( &pBufferCursor, 711 | FP_API_ACCEPTED_SUFFIX, 712 | FP_API_LENGTH_ACCEPTED_SUFFIX ); 713 | } 714 | 715 | if( topic == FleetProvisioningRejected ) 716 | { 717 | writeTopicFragmentAndAdvance( &pBufferCursor, 718 | FP_API_REJECTED_SUFFIX, 719 | FP_API_LENGTH_REJECTED_SUFFIX ); 720 | } 721 | 722 | *pOutLength = topicLength; 723 | } 724 | 725 | return status; 726 | } 727 | /*-----------------------------------------------------------*/ 728 | 729 | FleetProvisioningStatus_t FleetProvisioning_MatchTopic( const char * pTopic, 730 | uint16_t topicLength, 731 | FleetProvisioningTopic_t * pOutApi ) 732 | { 733 | FleetProvisioningStatus_t ret = FleetProvisioningNoMatch; 734 | 735 | if( ( pTopic == NULL ) || ( pOutApi == NULL ) ) 736 | { 737 | ret = FleetProvisioningBadParameter; 738 | LogError( ( "Invalid input parameter. pTopic: %p, pOutApi: %p.", 739 | ( const void * ) pTopic, 740 | ( void * ) pOutApi ) ); 741 | } 742 | else 743 | { 744 | *pOutApi = parseCreateCertificateFromCsrTopic( pTopic, topicLength ); 745 | 746 | if( *pOutApi == FleetProvisioningInvalidTopic ) 747 | { 748 | *pOutApi = parseCreateKeysAndCertificateTopic( pTopic, topicLength ); 749 | } 750 | 751 | if( *pOutApi == FleetProvisioningInvalidTopic ) 752 | { 753 | *pOutApi = parseRegisterThingTopic( pTopic, topicLength ); 754 | } 755 | 756 | if( *pOutApi != FleetProvisioningInvalidTopic ) 757 | { 758 | ret = FleetProvisioningSuccess; 759 | } 760 | } 761 | 762 | return ret; 763 | } 764 | /*-----------------------------------------------------------*/ 765 | -------------------------------------------------------------------------------- /source/include/fleet_provisioning.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AWS IoT Fleet Provisioning v1.2.1 3 | * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | * this software and associated documentation files (the "Software"), to deal in 9 | * the Software without restriction, including without limitation the rights to 10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | * the Software, and to permit persons to whom the Software is furnished to do so, 12 | * subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @file fleet_provisioning.h 27 | * @brief Interface for the AWS IoT Fleet Provisioning Library. 28 | */ 29 | 30 | #ifndef FLEET_PROVISIONING_H_ 31 | #define FLEET_PROVISIONING_H_ 32 | 33 | /* Standard includes. */ 34 | #include 35 | 36 | /* *INDENT-OFF* */ 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | /* *INDENT-ON* */ 41 | 42 | /* FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG allows building the Fleet 43 | * Provisioning library without a config file. If a config file is provided, 44 | * FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG macro must not be defined. 45 | */ 46 | #ifndef FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG 47 | #include "fleet_provisioning_config.h" 48 | #endif 49 | 50 | /* Default config values. */ 51 | #include "fleet_provisioning_config_defaults.h" 52 | 53 | /** 54 | * @ingroup fleet_provisioning_enum_types 55 | * @brief Return codes for Fleet Provisioning APIs. 56 | */ 57 | typedef enum 58 | { 59 | FleetProvisioningError = 0, 60 | FleetProvisioningSuccess, 61 | FleetProvisioningNoMatch, 62 | FleetProvisioningBadParameter, 63 | FleetProvisioningBufferTooSmall 64 | } FleetProvisioningStatus_t; 65 | 66 | /** 67 | * @ingroup fleet_provisioning_enum_types 68 | * @brief Fleet Provisioning topic values. 69 | */ 70 | typedef enum 71 | { 72 | FleetProvisioningInvalidTopic = 0, 73 | FleetProvJsonCreateCertFromCsrPublish, 74 | FleetProvJsonCreateCertFromCsrAccepted, 75 | FleetProvJsonCreateCertFromCsrRejected, 76 | FleetProvJsonCreateKeysAndCertPublish, 77 | FleetProvJsonCreateKeysAndCertAccepted, 78 | FleetProvJsonCreateKeysAndCertRejected, 79 | FleetProvJsonRegisterThingPublish, 80 | FleetProvJsonRegisterThingAccepted, 81 | FleetProvJsonRegisterThingRejected, 82 | FleetProvCborCreateCertFromCsrPublish, 83 | FleetProvCborCreateCertFromCsrAccepted, 84 | FleetProvCborCreateCertFromCsrRejected, 85 | FleetProvCborCreateKeysAndCertPublish, 86 | FleetProvCborCreateKeysAndCertAccepted, 87 | FleetProvCborCreateKeysAndCertRejected, 88 | FleetProvCborRegisterThingPublish, 89 | FleetProvCborRegisterThingAccepted, 90 | FleetProvCborRegisterThingRejected 91 | } FleetProvisioningTopic_t; 92 | 93 | /** 94 | * @ingroup fleet_provisioning_enum_types 95 | * @brief Topics for each Fleet Provisioning APIs. 96 | */ 97 | typedef enum 98 | { 99 | FleetProvisioningPublish, 100 | FleetProvisioningAccepted, 101 | FleetProvisioningRejected 102 | } FleetProvisioningApiTopics_t; 103 | 104 | /** 105 | * @ingroup fleet_provisioning_enum_types 106 | * @brief Message format for Fleet Provisioning APIs. 107 | */ 108 | typedef enum 109 | { 110 | FleetProvisioningJson, 111 | FleetProvisioningCbor 112 | } FleetProvisioningFormat_t; 113 | 114 | /*-----------------------------------------------------------*/ 115 | 116 | /** 117 | * @ingroup fleet_provisioning_constants 118 | * @brief Maximum length of a thing's name as permitted by AWS IoT Core. 119 | */ 120 | #define FP_TEMPLATENAME_MAX_LENGTH 36U 121 | 122 | /*-----------------------------------------------------------*/ 123 | 124 | /** 125 | * @cond DOXYGEN_IGNORE 126 | * Doxygen should ignore these macros as they are private. 127 | */ 128 | 129 | #define FP_CREATE_CERT_API_PREFIX "$aws/certificates/create-from-csr/" 130 | #define FP_CREATE_CERT_API_LENGTH_PREFIX ( ( uint16_t ) ( sizeof( FP_CREATE_CERT_API_PREFIX ) - 1U ) ) 131 | 132 | #define FP_CREATE_KEYS_API_PREFIX "$aws/certificates/create/" 133 | #define FP_CREATE_KEYS_API_LENGTH_PREFIX ( ( uint16_t ) ( sizeof( FP_CREATE_KEYS_API_PREFIX ) - 1U ) ) 134 | 135 | #define FP_REGISTER_API_PREFIX "$aws/provisioning-templates/" 136 | #define FP_REGISTER_API_LENGTH_PREFIX ( ( uint16_t ) ( sizeof( FP_REGISTER_API_PREFIX ) - 1U ) ) 137 | 138 | #define FP_REGISTER_API_BRIDGE "/provision/" 139 | #define FP_REGISTER_API_LENGTH_BRIDGE ( ( uint16_t ) ( sizeof( FP_REGISTER_API_BRIDGE ) - 1U ) ) 140 | 141 | #define FP_API_JSON_FORMAT "json" 142 | #define FP_API_LENGTH_JSON_FORMAT ( ( uint16_t ) ( sizeof( FP_API_JSON_FORMAT ) - 1U ) ) 143 | 144 | #define FP_API_CBOR_FORMAT "cbor" 145 | #define FP_API_LENGTH_CBOR_FORMAT ( ( uint16_t ) ( sizeof( FP_API_CBOR_FORMAT ) - 1U ) ) 146 | 147 | #define FP_API_ACCEPTED_SUFFIX "/accepted" 148 | #define FP_API_LENGTH_ACCEPTED_SUFFIX ( ( uint16_t ) ( sizeof( FP_API_ACCEPTED_SUFFIX ) - 1U ) ) 149 | 150 | #define FP_API_REJECTED_SUFFIX "/rejected" 151 | #define FP_API_LENGTH_REJECTED_SUFFIX ( ( uint16_t ) ( sizeof( FP_API_REJECTED_SUFFIX ) - 1U ) ) 152 | 153 | /** @endcond */ 154 | 155 | /*-----------------------------------------------------------*/ 156 | 157 | /* Fleet Provisioning CreateCertificateFromCSR macros */ 158 | 159 | /** 160 | * @brief Topic string for publishing a JSON CreateCertificateFromCSR request. 161 | */ 162 | #define FP_JSON_CREATE_CERT_PUBLISH_TOPIC \ 163 | ( FP_CREATE_CERT_API_PREFIX \ 164 | FP_API_JSON_FORMAT ) 165 | 166 | /** 167 | * @brief Topic string for getting a JSON CreateCertificateFromCSR accepted response. 168 | */ 169 | #define FP_JSON_CREATE_CERT_ACCEPTED_TOPIC \ 170 | ( FP_CREATE_CERT_API_PREFIX \ 171 | FP_API_JSON_FORMAT \ 172 | FP_API_ACCEPTED_SUFFIX ) 173 | 174 | /** 175 | * @brief Topic string for getting a JSON CreateCertificateFromCSR error response. 176 | */ 177 | #define FP_JSON_CREATE_CERT_REJECTED_TOPIC \ 178 | ( FP_CREATE_CERT_API_PREFIX \ 179 | FP_API_JSON_FORMAT \ 180 | FP_API_REJECTED_SUFFIX ) 181 | 182 | /** 183 | * @brief Topic string for publishing a CBOR CreateCertificateFromCSR request. 184 | */ 185 | #define FP_CBOR_CREATE_CERT_PUBLISH_TOPIC \ 186 | ( FP_CREATE_CERT_API_PREFIX \ 187 | FP_API_CBOR_FORMAT ) 188 | 189 | /** 190 | * @brief Topic string for getting a CBOR CreateCertificateFromCSR accepted response. 191 | */ 192 | #define FP_CBOR_CREATE_CERT_ACCEPTED_TOPIC \ 193 | ( FP_CREATE_CERT_API_PREFIX \ 194 | FP_API_CBOR_FORMAT \ 195 | FP_API_ACCEPTED_SUFFIX ) 196 | 197 | /** 198 | * @brief Topic string for getting a CBOR CreateCertificateFromCSR error response. 199 | */ 200 | #define FP_CBOR_CREATE_CERT_REJECTED_TOPIC \ 201 | ( FP_CREATE_CERT_API_PREFIX \ 202 | FP_API_CBOR_FORMAT \ 203 | FP_API_REJECTED_SUFFIX ) 204 | 205 | /** 206 | * @brief Length of topic string for publishing a JSON CreateCertificateFromCSR request. 207 | */ 208 | #define FP_JSON_CREATE_CERT_PUBLISH_LENGTH \ 209 | ( ( uint16_t ) ( sizeof( FP_JSON_CREATE_CERT_PUBLISH_TOPIC ) - 1U ) ) 210 | 211 | /** 212 | * @brief Length of topic string for getting a JSON CreateCertificateFromCSR accepted response. 213 | */ 214 | #define FP_JSON_CREATE_CERT_ACCEPTED_LENGTH \ 215 | ( ( uint16_t ) ( sizeof( FP_JSON_CREATE_CERT_ACCEPTED_TOPIC ) - 1U ) ) 216 | 217 | /** 218 | * @brief Length of topic string for getting a JSON CreateCertificateFromCSR error response. 219 | */ 220 | #define FP_JSON_CREATE_CERT_REJECTED_LENGTH \ 221 | ( ( uint16_t ) ( sizeof( FP_JSON_CREATE_CERT_REJECTED_TOPIC ) - 1U ) ) 222 | 223 | /** 224 | * @brief Length of topic string for publishing a CBOR CreateCertificateFromCSR request. 225 | */ 226 | #define FP_CBOR_CREATE_CERT_PUBLISH_LENGTH \ 227 | ( ( uint16_t ) ( sizeof( FP_CBOR_CREATE_CERT_PUBLISH_TOPIC ) - 1U ) ) 228 | 229 | /** 230 | * @brief Length of topic string for getting a CBOR CreateCertificateFromCSR accepted response. 231 | */ 232 | #define FP_CBOR_CREATE_CERT_ACCEPTED_LENGTH \ 233 | ( ( uint16_t ) ( sizeof( FP_CBOR_CREATE_CERT_ACCEPTED_TOPIC ) - 1U ) ) 234 | 235 | /** 236 | * @brief Length of topic string for getting a CBOR CreateCertificateFromCSR error response. 237 | */ 238 | #define FP_CBOR_CREATE_CERT_REJECTED_LENGTH \ 239 | ( ( uint16_t ) ( sizeof( FP_CBOR_CREATE_CERT_REJECTED_TOPIC ) - 1U ) ) 240 | 241 | /*-----------------------------------------------------------*/ 242 | 243 | /* Fleet Provisioning CreateKeysAndCertificate macros */ 244 | 245 | /** 246 | * @brief Topic string for publishing a JSON CreateKeysAndCertificate request. 247 | */ 248 | #define FP_JSON_CREATE_KEYS_PUBLISH_TOPIC \ 249 | ( FP_CREATE_KEYS_API_PREFIX \ 250 | FP_API_JSON_FORMAT ) 251 | 252 | /** 253 | * @brief Topic string for getting a JSON CreateKeysAndCertificate accepted response. 254 | */ 255 | #define FP_JSON_CREATE_KEYS_ACCEPTED_TOPIC \ 256 | ( FP_CREATE_KEYS_API_PREFIX \ 257 | FP_API_JSON_FORMAT \ 258 | FP_API_ACCEPTED_SUFFIX ) 259 | 260 | /** 261 | * @brief Topic string for getting a JSON CreateKeysAndCertificate error 262 | * response. 263 | */ 264 | #define FP_JSON_CREATE_KEYS_REJECTED_TOPIC \ 265 | ( FP_CREATE_KEYS_API_PREFIX \ 266 | FP_API_JSON_FORMAT \ 267 | FP_API_REJECTED_SUFFIX ) 268 | 269 | /** 270 | * @brief Topic string for publishing a CBOR CreateKeysAndCertificate request. 271 | */ 272 | #define FP_CBOR_CREATE_KEYS_PUBLISH_TOPIC \ 273 | ( FP_CREATE_KEYS_API_PREFIX \ 274 | FP_API_CBOR_FORMAT ) 275 | 276 | /** 277 | * @brief Topic string for getting a CBOR CreateKeysAndCertificate accepted response. 278 | */ 279 | #define FP_CBOR_CREATE_KEYS_ACCEPTED_TOPIC \ 280 | ( FP_CREATE_KEYS_API_PREFIX \ 281 | FP_API_CBOR_FORMAT \ 282 | FP_API_ACCEPTED_SUFFIX ) 283 | 284 | /** 285 | * @brief Topic string for getting a CBOR CreateKeysAndCertificate error 286 | * response. 287 | */ 288 | #define FP_CBOR_CREATE_KEYS_REJECTED_TOPIC \ 289 | ( FP_CREATE_KEYS_API_PREFIX \ 290 | FP_API_CBOR_FORMAT \ 291 | FP_API_REJECTED_SUFFIX ) 292 | 293 | /** 294 | * @brief Length of topic string for publishing a JSON CreateKeysAndCertificate request. 295 | */ 296 | #define FP_JSON_CREATE_KEYS_PUBLISH_LENGTH \ 297 | ( ( uint16_t ) ( sizeof( FP_JSON_CREATE_KEYS_PUBLISH_TOPIC ) - 1U ) ) 298 | 299 | /** 300 | * @brief Length of topic string for getting a JSON CreateKeysAndCertificate accepted response. 301 | */ 302 | #define FP_JSON_CREATE_KEYS_ACCEPTED_LENGTH \ 303 | ( ( uint16_t ) ( sizeof( FP_JSON_CREATE_KEYS_ACCEPTED_TOPIC ) - 1U ) ) 304 | 305 | /** 306 | * @brief Length of topic string for getting a JSON CreateKeysAndCertificate error response. 307 | */ 308 | #define FP_JSON_CREATE_KEYS_REJECTED_LENGTH \ 309 | ( ( uint16_t ) ( sizeof( FP_JSON_CREATE_KEYS_REJECTED_TOPIC ) - 1U ) ) 310 | 311 | /** 312 | * @brief Length of topic string for publishing a CBOR CreateKeysAndCertificate request. 313 | */ 314 | #define FP_CBOR_CREATE_KEYS_PUBLISH_LENGTH \ 315 | ( ( uint16_t ) ( sizeof( FP_CBOR_CREATE_KEYS_PUBLISH_TOPIC ) - 1U ) ) 316 | 317 | /** 318 | * @brief Length of topic string for getting a CBOR CreateKeysAndCertificate accepted response. 319 | */ 320 | #define FP_CBOR_CREATE_KEYS_ACCEPTED_LENGTH \ 321 | ( ( uint16_t ) ( sizeof( FP_CBOR_CREATE_KEYS_ACCEPTED_TOPIC ) - 1U ) ) 322 | 323 | /** 324 | * @brief Length of topic string for getting a CBOR CreateKeysAndCertificate error response. 325 | */ 326 | #define FP_CBOR_CREATE_KEYS_REJECTED_LENGTH \ 327 | ( ( uint16_t ) ( sizeof( FP_CBOR_CREATE_KEYS_REJECTED_TOPIC ) - 1U ) ) 328 | 329 | /*-----------------------------------------------------------*/ 330 | 331 | /* Fleet Provisioning RegisterThing macros */ 332 | 333 | 334 | /** 335 | * @brief Topic string for publishing a JSON RegisterThing request. 336 | * 337 | * This macro should be used when the provisioning template name is known at 338 | * compile time. If the provisioning template name is not known at compile time, 339 | * the #FleetProvisioning_GetRegisterThingTopic API should be used instead. 340 | * 341 | * @param templateName The name of the provisioning template to use. 342 | */ 343 | #define FP_JSON_REGISTER_PUBLISH_TOPIC( templateName ) \ 344 | ( FP_REGISTER_API_PREFIX \ 345 | templateName \ 346 | FP_REGISTER_API_BRIDGE \ 347 | FP_API_JSON_FORMAT ) 348 | 349 | /** 350 | * @brief Topic string for getting a JSON RegisterThing accepted response. 351 | * 352 | * This macro should be used when the provisioning template name is known at 353 | * compile time. If the provisioning template name is not known at compile time, 354 | * the #FleetProvisioning_GetRegisterThingTopic API should be used instead. 355 | * 356 | * @param templateName The name of the provisioning template to use. 357 | */ 358 | #define FP_JSON_REGISTER_ACCEPTED_TOPIC( templateName ) \ 359 | ( FP_REGISTER_API_PREFIX \ 360 | templateName \ 361 | FP_REGISTER_API_BRIDGE \ 362 | FP_API_JSON_FORMAT \ 363 | FP_API_ACCEPTED_SUFFIX ) 364 | 365 | /** 366 | * @brief Topic string for getting a JSON RegisterThing error response. 367 | * 368 | * This macro should be used when the provisioning template name is known at 369 | * compile time. If the provisioning template name is not known at compile time, 370 | * the #FleetProvisioning_GetRegisterThingTopic API should be used instead. 371 | * 372 | * @param templateName The name of the provisioning template to use. 373 | */ 374 | #define FP_JSON_REGISTER_REJECTED_TOPIC( templateName ) \ 375 | ( FP_REGISTER_API_PREFIX \ 376 | templateName \ 377 | FP_REGISTER_API_BRIDGE \ 378 | FP_API_JSON_FORMAT \ 379 | FP_API_REJECTED_SUFFIX ) 380 | 381 | /** 382 | * @brief Topic string for publishing a CBOR RegisterThing request. 383 | * 384 | * This macro should be used when the provisioning template name is known at 385 | * compile time. If the provisioning template name is not known at compile time, 386 | * the #FleetProvisioning_GetRegisterThingTopic API should be used instead. 387 | * 388 | * @param templateName The name of the provisioning template to use. 389 | */ 390 | #define FP_CBOR_REGISTER_PUBLISH_TOPIC( templateName ) \ 391 | ( FP_REGISTER_API_PREFIX \ 392 | templateName \ 393 | FP_REGISTER_API_BRIDGE \ 394 | FP_API_CBOR_FORMAT ) 395 | 396 | /** 397 | * @brief Topic string for getting a CBOR RegisterThing accepted response. 398 | * 399 | * This macro should be used when the provisioning template name is known at 400 | * compile time. If the provisioning template name is not known at compile time, 401 | * the #FleetProvisioning_GetRegisterThingTopic API should be used instead. 402 | * 403 | * @param templateName The name of the provisioning template to use. 404 | */ 405 | #define FP_CBOR_REGISTER_ACCEPTED_TOPIC( templateName ) \ 406 | ( FP_REGISTER_API_PREFIX \ 407 | templateName \ 408 | FP_REGISTER_API_BRIDGE \ 409 | FP_API_CBOR_FORMAT \ 410 | FP_API_ACCEPTED_SUFFIX ) 411 | 412 | /** 413 | * @brief Topic string for getting a CBOR RegisterThing error response. 414 | * 415 | * This macro should be used when the provisioning template name is known at 416 | * compile time. If the provisioning template name is not known at compile time, 417 | * the #FleetProvisioning_GetRegisterThingTopic API should be used instead. 418 | * 419 | * @param templateName The name of the provisioning template to use. 420 | */ 421 | #define FP_CBOR_REGISTER_REJECTED_TOPIC( templateName ) \ 422 | ( FP_REGISTER_API_PREFIX \ 423 | templateName \ 424 | FP_REGISTER_API_BRIDGE \ 425 | FP_API_CBOR_FORMAT \ 426 | FP_API_REJECTED_SUFFIX ) 427 | 428 | /** 429 | * @brief Length of topic string for publishing a JSON RegisterThing request. 430 | * 431 | * This macro should be used when the provisioning template name is known at 432 | * compile time. If the provisioning template name is not known at compile time, 433 | * the #FleetProvisioning_GetRegisterThingTopic API should be used instead. 434 | * 435 | * @param templateNameLength The length of the provisioning template name. 436 | */ 437 | #define FP_JSON_REGISTER_PUBLISH_LENGTH( templateNameLength ) \ 438 | ( FP_REGISTER_API_LENGTH_PREFIX + \ 439 | ( templateNameLength ) + \ 440 | FP_REGISTER_API_LENGTH_BRIDGE + \ 441 | FP_API_LENGTH_JSON_FORMAT ) 442 | 443 | /** 444 | * @brief Length of topic string for getting a JSON RegisterThing accepted response. 445 | * 446 | * This macro should be used when the provisioning template name is known at 447 | * compile time. If the provisioning template name is not known at compile time, 448 | * the #FleetProvisioning_GetRegisterThingTopic API should be used instead. 449 | * 450 | * @param templateNameLength The length of the provisioning template name. 451 | */ 452 | #define FP_JSON_REGISTER_ACCEPTED_LENGTH( templateNameLength ) \ 453 | ( FP_REGISTER_API_LENGTH_PREFIX + \ 454 | ( templateNameLength ) + \ 455 | FP_REGISTER_API_LENGTH_BRIDGE + \ 456 | FP_API_LENGTH_JSON_FORMAT + \ 457 | FP_API_LENGTH_ACCEPTED_SUFFIX ) 458 | 459 | /** 460 | * @brief Length of topic string for getting a JSON RegisterThing error response. 461 | * 462 | * This macro should be used when the provisioning template name is known at 463 | * compile time. If the provisioning template name is not known at compile time, 464 | * the #FleetProvisioning_GetRegisterThingTopic API should be used instead. 465 | * 466 | * @param templateNameLength The length of the provisioning template name. 467 | */ 468 | #define FP_JSON_REGISTER_REJECTED_LENGTH( templateNameLength ) \ 469 | ( FP_REGISTER_API_LENGTH_PREFIX + \ 470 | ( templateNameLength ) + \ 471 | FP_REGISTER_API_LENGTH_BRIDGE + \ 472 | FP_API_LENGTH_JSON_FORMAT + \ 473 | FP_API_LENGTH_REJECTED_SUFFIX ) 474 | 475 | /** 476 | * @brief Length of topic string for publishing a CBOR RegisterThing request. 477 | * 478 | * This macro should be used when the provisioning template name is known at 479 | * compile time. If the provisioning template name is not known at compile time, 480 | * the #FleetProvisioning_GetRegisterThingTopic API should be used instead. 481 | * 482 | * @param templateNameLength The length of the provisioning template name. 483 | */ 484 | #define FP_CBOR_REGISTER_PUBLISH_LENGTH( templateNameLength ) \ 485 | ( FP_REGISTER_API_LENGTH_PREFIX + \ 486 | ( templateNameLength ) + \ 487 | FP_REGISTER_API_LENGTH_BRIDGE + \ 488 | FP_API_LENGTH_CBOR_FORMAT ) 489 | 490 | /** 491 | * @brief Length of topic string for getting a CBOR RegisterThing accepted response. 492 | * 493 | * This macro should be used when the provisioning template name is known at 494 | * compile time. If the provisioning template name is not known at compile time, 495 | * the #FleetProvisioning_GetRegisterThingTopic API should be used instead. 496 | * 497 | * @param templateNameLength The length of the provisioning template name. 498 | */ 499 | #define FP_CBOR_REGISTER_ACCEPTED_LENGTH( templateNameLength ) \ 500 | ( FP_REGISTER_API_LENGTH_PREFIX + \ 501 | ( templateNameLength ) + \ 502 | FP_REGISTER_API_LENGTH_BRIDGE + \ 503 | FP_API_LENGTH_CBOR_FORMAT + \ 504 | FP_API_LENGTH_ACCEPTED_SUFFIX ) 505 | 506 | /** 507 | * @brief Length of topic string for getting a CBOR RegisterThing error response. 508 | * 509 | * This macro should be used when the provisioning template name is known at 510 | * compile time. If the provisioning template name is not known at compile time, 511 | * the #FleetProvisioning_GetRegisterThingTopic API should be used instead. 512 | * 513 | * @param templateNameLength The length of the provisioning template name. 514 | */ 515 | #define FP_CBOR_REGISTER_REJECTED_LENGTH( templateNameLength ) \ 516 | ( FP_REGISTER_API_LENGTH_PREFIX + \ 517 | ( templateNameLength ) + \ 518 | FP_REGISTER_API_LENGTH_BRIDGE + \ 519 | FP_API_LENGTH_CBOR_FORMAT + \ 520 | FP_API_LENGTH_REJECTED_SUFFIX ) 521 | 522 | /*-----------------------------------------------------------*/ 523 | 524 | /* Key names for Fleet Provisioning MQTT API JSON/CBOR payloads. */ 525 | 526 | /** 527 | * @brief Key for the PEM-encoded certificate signing request in the 528 | * CreateCertificateFromCSR request payload. 529 | */ 530 | #define FP_API_CSR_KEY "certificateSigningRequest" 531 | 532 | /** 533 | * @brief Key for the certificate ownership token in the 534 | * CreateCertificateFromCSR and CreateKeysAndCertificate response payloads, and 535 | * the RegisterThing request payload. 536 | */ 537 | #define FP_API_OWNERSHIP_TOKEN_KEY "certificateOwnershipToken" 538 | 539 | /** 540 | * @brief Key for the certificate ID in the CreateCertificateFromCSR and 541 | * CreateKeysAndCertificate response payloads. 542 | */ 543 | #define FP_API_CERTIFICATE_ID_KEY "certificateId" 544 | 545 | /** 546 | * @brief Key for the PEM-encoded certificate in the CreateCertificateFromCSR 547 | * and CreateKeysAndCertificate response payloads. 548 | */ 549 | #define FP_API_CERTIFICATE_PEM_KEY "certificatePem" 550 | 551 | /** 552 | * @brief Key for the private key in the CreateKeysAndCertificate response 553 | * payload. 554 | */ 555 | #define FP_API_PRIVATE_KEY_KEY "privateKey" 556 | 557 | /** 558 | * @brief Key for the optional parameters in the RegisterThing request payload. 559 | */ 560 | #define FP_API_PARAMETERS_KEY "parameters" 561 | 562 | /** 563 | * @brief Key for the template-defined device configuration in the 564 | * RegisterThing response payload. 565 | */ 566 | #define FP_API_DEVICE_CONFIG_KEY "deviceConfiguration" 567 | 568 | /** 569 | * @brief Key for the name of the created AWS IoT Thing in the RegisterThing 570 | * response payload. 571 | */ 572 | #define FP_API_THING_NAME_KEY "thingName" 573 | 574 | /** 575 | * @brief Key for the status code in Fleet Provisioning error response 576 | * payloads. 577 | */ 578 | #define FP_API_STATUS_CODE_KEY "statusCode" 579 | 580 | /** 581 | * @brief Key for the error code in Fleet Provisioning error response payloads. 582 | */ 583 | #define FP_API_ERROR_CODE_KEY "errorCode" 584 | 585 | /** 586 | * @brief Key for the error message in Fleet Provisioning error response 587 | * payloads. 588 | */ 589 | #define FP_API_ERROR_MESSAGE_KEY "errorMessage" 590 | 591 | /*-----------------------------------------------------------*/ 592 | 593 | /** 594 | * @brief Populate the topic string for a Fleet Provisioning RegisterThing topic. 595 | * 596 | * @param[out] pTopicBuffer The buffer to write the topic string into. 597 | * @param[in] bufferLength The length of @p pTopicBuffer. 598 | * @param[in] format The desired RegisterThing format. 599 | * @param[in] topic The desired RegisterThing topic. 600 | * @param[in] pTemplateName The name of the provisioning template configured 601 | * with AWS IoT. 602 | * @param[in] templateNameLength The length of the provisioning template name. 603 | * @param[out] pOutLength The length of the topic string written to the buffer. 604 | * 605 | * @return FleetProvisioningSuccess if the topic string is written to the buffer; 606 | * FleetProvisioningBadParameter if invalid parameters, such as non-RegisterThing topics, are passed; 607 | * FleetProvisioningBufferTooSmall if the buffer cannot hold the full topic string. 608 | * 609 | * example 610 | * @code{c} 611 | * 612 | * // The following example shows how to use the FleetProvisioning_GetRegisterThingTopic 613 | * // function to generate a topic string for getting an accepted response for 614 | * // a JSON RegisterThing request. 615 | * 616 | * #define TOPIC_BUFFER_LENGTH ( 256u ) 617 | * 618 | * // In order to use the AWS IoT Fleet Provisioning service, there must be a 619 | * // provisioning template registered with AWS IoT Core. 620 | * // This example assumes that the template is named "template_name". 621 | * #define TEMPLATE_NAME "template_name" 622 | * #define TEMPLATE_NAME_LENGTH ( ( uint16_t ) ( sizeof( TEMPLATE_NAME ) - 1U ) 623 | * char pTopicbuffer[ TOPIC_BUFFER_LENGTH ] = { 0 }; 624 | * uint16_t topicLength = 0; 625 | * FleetProvisioningStatus_t status = FleetProvisioningError; 626 | * 627 | * status = FleetProvisioning_GetRegisterThingTopic( pTopicBuffer, 628 | * TOPIC_BUFFER_LENGTH, 629 | * FleetProvisioningJson, 630 | * FleetProvisioningAccepted, 631 | * TEMPLATE_NAME, 632 | * TEMPLATE_NAME_LENGTH, 633 | * &( topiclength ) ); 634 | * 635 | * if( status == FleetProvisioningSuccess ) 636 | * { 637 | * // The buffer pTopicBuffer contains the topic string of length 638 | * // topicLength for getting a response for an accepted JSON RegisterThing 639 | * // request. Subscribe to this topic using an MQTT library of your choice. 640 | * } 641 | * @endcode 642 | */ 643 | /* @[declare_fleet_provisioning_getregisterthingtopic] */ 644 | FleetProvisioningStatus_t FleetProvisioning_GetRegisterThingTopic( char * pTopicBuffer, 645 | uint16_t bufferLength, 646 | FleetProvisioningFormat_t format, 647 | FleetProvisioningApiTopics_t topic, 648 | const char * pTemplateName, 649 | uint16_t templateNameLength, 650 | uint16_t * pOutLength ); 651 | /* @[declare_fleet_provisioning_getregisterthingtopic] */ 652 | 653 | /*-----------------------------------------------------------*/ 654 | 655 | /** 656 | * @brief Check if the given topic is one of the Fleet Provisioning topics. 657 | * 658 | * The function outputs which API the topic is for. 659 | * 660 | * @param[in] pTopic The topic string to check. 661 | * @param[in] topicLength The length of the topic string. 662 | * @param[out] pOutApi The Fleet Provisioning topic API value. 663 | * 664 | * @return FleetProvisioningSuccess if the topic is one of the Fleet Provisioning topics; 665 | * FleetProvisioningBadParameter if invalid parameters are passed; 666 | * FleetProvisioningNoMatch if the topic is NOT one of the Fleet Provisioning topics (parameter 667 | * pOutApi gets FleetProvisioningInvalidTopic). 668 | * 669 | * Example 670 | * @code{c} 671 | * 672 | * // The following example shows how to use the FleetProvisioning_MatchTopic 673 | * // function to check if an incoming MQTT publish message is a Fleet 674 | * // Provisioning message. 675 | * 676 | * FleetProvisioningTopic_t api; 677 | * FleetProvisioningStatus_t status = FleetProvisioningError; 678 | * 679 | * // pTopic and topicLength are the topic string and length of the topic on 680 | * // which the publish message is received. These are usually provided by the 681 | * // MQTT library used. 682 | * status = FleetProvisioning_MatchTopic( pTopic 683 | * topicLength, 684 | * &( api ) ); 685 | * 686 | * if( status == FleetProvisioningSuccess ) 687 | * { 688 | * if( api == FleetProvJsonCreateCertFromCsrAccepted ) 689 | * { 690 | * // The published JSON request was accepted and completed by the AWS 691 | * // IoT Fleet Provisioning service. You can parse the response using 692 | * // your choice of JSON parser get the certificate, ID, and ownership 693 | * // token. 694 | * } 695 | * else if( api == FleetProvJsonCreateCertFromCsrRejected ) 696 | * { 697 | * // The published JSON request was rejected by the AWS IoT Fleet 698 | * // Provisioning service. 699 | * } 700 | * else 701 | * { 702 | * // Unexpected response. 703 | * } 704 | * } 705 | * @endcode 706 | */ 707 | /* @[declare_fleet_provisioning_matchtopic] */ 708 | FleetProvisioningStatus_t FleetProvisioning_MatchTopic( const char * pTopic, 709 | uint16_t topicLength, 710 | FleetProvisioningTopic_t * pOutApi ); 711 | /* @[declare_fleet_provisioning_matchtopic] */ 712 | 713 | /*-----------------------------------------------------------*/ 714 | 715 | /* *INDENT-OFF* */ 716 | #ifdef __cplusplus 717 | } 718 | #endif 719 | /* *INDENT-ON* */ 720 | 721 | #endif /* FLEET_PROVISIONING_H_ */ 722 | -------------------------------------------------------------------------------- /source/include/fleet_provisioning_config_defaults.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AWS IoT Fleet Provisioning v1.2.1 3 | * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | * this software and associated documentation files (the "Software"), to deal in 9 | * the Software without restriction, including without limitation the rights to 10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | * the Software, and to permit persons to whom the Software is furnished to do so, 12 | * subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @file fleet_provisioning_config_defaults.h 27 | * @brief Default config values for the AWS IoT Fleet Provisioning Library. 28 | */ 29 | 30 | #ifndef FLEET_PROVISIONING_CONFIG_DEFAULTS_H_ 31 | #define FLEET_PROVISIONING_CONFIG_DEFAULTS_H_ 32 | 33 | /* The macro definition for FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG is for 34 | * Doxygen documentation only. */ 35 | 36 | /** 37 | * @brief Define this macro to build the AWS IoT Fleet Provisioning Library 38 | * without the custom config file fleet_provisioning_config.h. 39 | * 40 | * Without the custom config, the the AWS IoT Fleet Provisioning Library builds 41 | * with default values of config macros defined in the 42 | * fleet_provisioning_config_defaults.h file. 43 | * 44 | * If a custom config file is provided, then 45 | * FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG must not be defined. 46 | * 47 | * Default value: FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG is 48 | * not defined by default and the library expects a 49 | * fleet_provisioning_config.h file. 50 | */ 51 | #ifdef DOXYGEN 52 | #define FLEET_PROVISIONING_DO_NOT_USE_CUSTOM_CONFIG 53 | #endif 54 | 55 | /** 56 | * @brief Macro used in the Fleet Provisioning library to log error messages. 57 | * 58 | * To enable error logging, this macro should be mapped to an 59 | * application-specific logging implementation. 60 | * 61 | * @note This logging macro is called in the Fleet Provisioning library with 62 | * parameters wrapped in double parentheses to be ISO C89/C90 standard 63 | * compliant. For a reference POSIX implementation of the logging macros, refer 64 | * to the fleet_provisioning_config.h file, and the logging-stack in demos 65 | * folder of the [AWS IoT Embedded C SDK 66 | * repository](https://github.com/aws/aws-iot-device-sdk-embedded-C). 67 | * 68 | * Default value: Error logs are turned off, and no code is generated for 69 | * calls to the macro in the Fleet Provisioning library on compilation. 70 | */ 71 | #ifndef LogError 72 | #define LogError( message ) 73 | #endif 74 | 75 | /** 76 | * @brief Macro used in the Fleet Provisioning library to log warning messages. 77 | * 78 | * To enable warning logging, this macro should be mapped to an 79 | * application-specific logging implementation. 80 | * 81 | * @note This logging macro is called in the Fleet Provisioning library with 82 | * parameters wrapped in double parentheses to be ISO C89/C90 standard 83 | * compliant. For a reference POSIX implementation of the logging macros, refer 84 | * to the fleet_provisioning_config.h file, and the logging-stack in demos 85 | * folder of the [AWS IoT Embedded C SDK 86 | * repository](https://github.com/aws/aws-iot-device-sdk-embedded-C). 87 | * 88 | * Default value: Warning logs are turned off, and no code is generated 89 | * for calls to the macro in the Fleet Provisioning library on compilation. 90 | */ 91 | #ifndef LogWarn 92 | #define LogWarn( message ) 93 | #endif 94 | 95 | /** 96 | * @brief Macro used in the Fleet Provisioning library to log info messages. 97 | * 98 | * To enable info logging, this macro should be mapped to an 99 | * application-specific logging implementation. 100 | * 101 | * @note This logging macro is called in the Fleet Provisioning library with 102 | * parameters wrapped in double parentheses to be ISO C89/C90 standard 103 | * compliant. For a reference POSIX implementation of the logging macros, refer 104 | * to the fleet_provisioning_config.h file, and the logging-stack in demos 105 | * folder of the [AWS IoT Embedded C SDK 106 | * repository](https://github.com/aws/aws-iot-device-sdk-embedded-C). 107 | * 108 | * Default value: Info logs are turned off, and no code is generated for 109 | * calls to the macro in the Fleet Provisioning library on compilation. 110 | */ 111 | #ifndef LogInfo 112 | #define LogInfo( message ) 113 | #endif 114 | 115 | /** 116 | * @brief Macro used in the Fleet Provisioning library to log debug messages. 117 | * 118 | * To enable debug logging, this macro should be mapped to an 119 | * application-specific logging implementation. 120 | * 121 | * @note This logging macro is called in the Fleet Provisioning library with 122 | * parameters wrapped in double parentheses to be ISO C89/C90 standard 123 | * compliant. For a reference POSIX implementation of the logging macros, refer 124 | * to the fleet_provisioning_config.h file, and the logging-stack in demos 125 | * folder of the [AWS IoT Embedded C SDK 126 | * repository](https://github.com/aws/aws-iot-device-sdk-embedded-C). 127 | * 128 | * Default value: Debug logs are turned off, and no code is generated for 129 | * calls to the macro in the Fleet Provisioning library on compilation. 130 | */ 131 | #ifndef LogDebug 132 | #define LogDebug( message ) 133 | #endif 134 | 135 | #endif /* FLEET_PROVISIONING_CONFIG_DEFAULTS_H_ */ 136 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.22.0 ) 2 | project ( "Fleet Provisioning unit test" 3 | VERSION 1.2.1 4 | LANGUAGES C ) 5 | 6 | # Allow the project to be organized into folders. 7 | set_property( GLOBAL PROPERTY USE_FOLDERS ON ) 8 | 9 | # Use C90. 10 | set( CMAKE_C_STANDARD 90 ) 11 | set( CMAKE_C_STANDARD_REQUIRED ON ) 12 | 13 | # If no configuration is defined, turn everything on. 14 | if( NOT DEFINED COV_ANALYSIS AND NOT DEFINED UNITTEST ) 15 | set( COV_ANALYSIS TRUE ) 16 | set( UNITTEST TRUE ) 17 | endif() 18 | 19 | # Do not allow in-source build. 20 | if( ${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR} ) 21 | message( FATAL_ERROR "In-source build is not allowed. Please build in a separate directory, such as ${PROJECT_SOURCE_DIR}/build." ) 22 | endif() 23 | 24 | # Set global path variable. 25 | get_filename_component( __MODULE_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE ) 26 | set( MODULE_ROOT_DIR ${__MODULE_ROOT_DIR} CACHE INTERNAL "Fleet Provisioning repository root." ) 27 | set( UNIT_TEST_DIR ${MODULE_ROOT_DIR}/test/unit-test CACHE INTERNAL "Fleet Provisioning unit test directory." ) 28 | set( UNITY_DIR ${UNIT_TEST_DIR}/Unity CACHE INTERNAL "Unity library source directory." ) 29 | 30 | # Configure options to always show in CMake GUI. 31 | option( BUILD_CLONE_SUBMODULES 32 | "Set this to ON to automatically clone any required Git submodules. When OFF, submodules must be manually cloned." 33 | ON ) 34 | 35 | # Set output directories. 36 | set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin ) 37 | set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib ) 38 | set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib ) 39 | 40 | # ====================== Coverity Analysis Configuration ====================== 41 | 42 | if( COV_ANALYSIS ) 43 | # Include filepaths for source and include. 44 | include( ${MODULE_ROOT_DIR}/fleetprovisioningFilePaths.cmake ) 45 | 46 | # Target for Coverity analysis that builds the library. 47 | add_library( coverity_analysis 48 | ${FLEET_PROVISIONING_SOURCES} ) 49 | 50 | # Fleet Provisioning public include path and test config file. 51 | target_include_directories( coverity_analysis PUBLIC 52 | ${FLEET_PROVISIONING_INCLUDE_PUBLIC_DIRS} 53 | "${CMAKE_CURRENT_LIST_DIR}/include" ) 54 | 55 | 56 | # Build without debug enabled when performing static analysis 57 | target_compile_options(coverity_analysis PUBLIC -DNDEBUG -DDISABLE_LOGGING) 58 | endif() 59 | 60 | # ============================ Test Configuration ============================ 61 | 62 | if( UNITTEST ) 63 | # Include Unity build configuration. 64 | include( unit-test/unity_build.cmake ) 65 | 66 | # Check if the Unity source directory exists. If it does not exist and the 67 | # BUILD_CLONE_SUBMODULES configuration is enabled, clone the Unity submodule. 68 | if( NOT EXISTS ${UNITY_DIR}/src ) 69 | # Attempt to clone Unity. 70 | if( ${BUILD_CLONE_SUBMODULES} ) 71 | clone_unity() 72 | else() 73 | message( FATAL_ERROR "The required submodule Unity does not exist. Either clone it manually, or set BUILD_CLONE_SUBMODULES to 1 to automatically clone it during build." ) 74 | endif() 75 | endif() 76 | 77 | # Use CTest utility for managing test runs. 78 | enable_testing() 79 | 80 | # Add build target for Unity, required for unit testing. 81 | add_unity_target() 82 | 83 | # Add functions to enable Unity based tests and coverage. 84 | include( ${MODULE_ROOT_DIR}/tools/unity/create_test.cmake ) 85 | 86 | # Include build configuration for unit tests. 87 | add_subdirectory( unit-test ) 88 | 89 | # ====================== Coverage Analysis configuration ====================== 90 | 91 | # Add a target for running coverage on tests. 92 | add_custom_target( coverage 93 | COMMAND ${CMAKE_COMMAND} -DUNITY_DIR=${UNITY_DIR} 94 | -P ${MODULE_ROOT_DIR}/tools/unity/coverage.cmake 95 | DEPENDS unity fleet_provisioning_utest 96 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) 97 | endif() 98 | -------------------------------------------------------------------------------- /test/cbmc/.gitignore: -------------------------------------------------------------------------------- 1 | # Emitted when running CBMC proofs 2 | proofs/**/logs 3 | proofs/**/gotos 4 | proofs/**/report 5 | proofs/**/html 6 | proofs/output 7 | 8 | # Emitted by CBMC Viewer 9 | TAGS-* 10 | 11 | # Emitted by Arpa 12 | arpa_cmake/ 13 | arpa-validation-logs/ 14 | Makefile.arpa 15 | 16 | # Emitted by litani 17 | .ninja_deps 18 | .ninja_log 19 | .litani_cache_dir 20 | 21 | # These files should be overwritten whenever prepare.py runs 22 | cbmc-batch.yaml 23 | 24 | __pycache__/ 25 | -------------------------------------------------------------------------------- /test/cbmc/include/README.md: -------------------------------------------------------------------------------- 1 | CBMC proof include files 2 | ======================== 3 | 4 | This directory contains include files written for CBMC proof. It is 5 | common to write some code to model aspects of the system under test, 6 | and the header files for this code go here. 7 | -------------------------------------------------------------------------------- /test/cbmc/proofs/FleetProvisioning_GetRegisterThingTopic/FleetProvisioning_GetRegisterThingTopic_harness.c: -------------------------------------------------------------------------------- 1 | /* 2 | * AWS IoT Fleet Provisioning v1.2.1 3 | * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | * this software and associated documentation files (the "Software"), to deal in 9 | * the Software without restriction, including without limitation the rights to 10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | * the Software, and to permit persons to whom the Software is furnished to do so, 12 | * subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @file FleetProvisioning_GetRegisterThingTopic_harness.c 27 | * @brief Implements the proof harness for FleetProvisioning_GetRegisterThingTopic function. 28 | */ 29 | 30 | #include 31 | #include "fleet_provisioning.h" 32 | 33 | void harness() 34 | { 35 | char * pTopicBuffer; 36 | uint16_t topicBufferLength; 37 | FleetProvisioningFormat_t format; 38 | FleetProvisioningApiTopics_t topic; 39 | const char * pTemplateName; 40 | uint16_t templateNameLength; 41 | uint16_t * pOutLength; 42 | 43 | __CPROVER_assume( topicBufferLength < CBMC_MAX_OBJECT_SIZE ); 44 | 45 | /* +1 is to ensure that we run the function for invalid template name 46 | * lengths as well. */ 47 | __CPROVER_assume( templateNameLength <= ( FP_TEMPLATENAME_MAX_LENGTH + 1 ) ); 48 | 49 | pTopicBuffer = malloc( topicBufferLength ); 50 | pTemplateName = malloc( templateNameLength ); 51 | pOutLength = malloc( sizeof( *pOutLength ) ); 52 | 53 | FleetProvisioning_GetRegisterThingTopic( pTopicBuffer, 54 | topicBufferLength, 55 | format, 56 | topic, 57 | pTemplateName, 58 | templateNameLength, 59 | pOutLength ); 60 | } 61 | -------------------------------------------------------------------------------- /test/cbmc/proofs/FleetProvisioning_GetRegisterThingTopic/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | HARNESS_ENTRY = harness 5 | HARNESS_FILE = FleetProvisioning_GetRegisterThingTopic_harness 6 | 7 | # This should be a unique identifier for this proof, and will appear on the 8 | # Litani dashboard. It can be human-readable and contain spaces if you wish. 9 | PROOF_UID = FleetProvisioning_GetRegisterThingTopic 10 | 11 | DEFINES += 12 | INCLUDES += 13 | 14 | REMOVE_FUNCTION_BODY += 15 | UNWINDSET += 16 | 17 | PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c 18 | PROJECT_SOURCES += $(SRCDIR)/source/fleet_provisioning.c 19 | 20 | include ../Makefile.common 21 | -------------------------------------------------------------------------------- /test/cbmc/proofs/FleetProvisioning_GetRegisterThingTopic/README.md: -------------------------------------------------------------------------------- 1 | FleetProvisioning_GetRegisterThingTopic proof 2 | ============== 3 | 4 | This directory contains a memory safety proof for FleetProvisioning_GetRegisterThingTopic. 5 | 6 | To run the proof. 7 | ------------- 8 | 9 | * Add `cbmc`, `goto-cc`, `goto-instrument`, `goto-analyzer`, and `cbmc-viewer` 10 | to your path. 11 | * Run `make`. 12 | * Open html/index.html in a web browser. 13 | 14 | To use [`arpa`](https://awslabs.github.io/aws-proof-build-assistant) to simplify writing Makefiles. 15 | ------------- 16 | 17 | * Run `make arpa` to generate a Makefile.arpa that contains relevant build information for the proof. 18 | * Use Makefile.arpa as the starting point for your proof Makefile by: 19 | 1. Modifying Makefile.arpa (if required). 20 | 2. Including Makefile.arpa into the existing proof Makefile (add `sinclude Makefile.arpa` at the bottom of the Makefile, right before `include ../Makefile.common`). 21 | -------------------------------------------------------------------------------- /test/cbmc/proofs/FleetProvisioning_GetRegisterThingTopic/cbmc-proof.txt: -------------------------------------------------------------------------------- 1 | # This file marks this directory as containing a CBMC proof. 2 | -------------------------------------------------------------------------------- /test/cbmc/proofs/FleetProvisioning_GetRegisterThingTopic/cbmc-viewer.json: -------------------------------------------------------------------------------- 1 | { "expected-missing-functions": 2 | [ 3 | 4 | ], 5 | "proof-name": "FleetProvisioning_GetRegisterThingTopic", 6 | "proof-root": "test/cbmc/proofs" 7 | } 8 | -------------------------------------------------------------------------------- /test/cbmc/proofs/FleetProvisioning_MatchTopic/FleetProvisioning_MatchTopic_harness.c: -------------------------------------------------------------------------------- 1 | /* 2 | * AWS IoT Fleet Provisioning v1.2.1 3 | * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | * this software and associated documentation files (the "Software"), to deal in 9 | * the Software without restriction, including without limitation the rights to 10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | * the Software, and to permit persons to whom the Software is furnished to do so, 12 | * subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @file FleetProvisioning_MatchTopic_harness.c 27 | * @brief Implements the proof harness for FleetProvisioning_MatchTopic function. 28 | */ 29 | 30 | #include 31 | #include "fleet_provisioning.h" 32 | 33 | void harness() 34 | { 35 | const char * pTopic; 36 | uint16_t topicLength; 37 | FleetProvisioningTopic_t * pOutApi; 38 | 39 | __CPROVER_assume( topicLength < TOPIC_STRING_LENGTH_MAX ); 40 | 41 | pTopic = malloc( topicLength ); 42 | pOutApi = malloc( sizeof( *pOutApi ) ); 43 | 44 | FleetProvisioning_MatchTopic( pTopic, 45 | topicLength, 46 | pOutApi ); 47 | } 48 | -------------------------------------------------------------------------------- /test/cbmc/proofs/FleetProvisioning_MatchTopic/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | HARNESS_ENTRY = harness 5 | HARNESS_FILE = FleetProvisioning_MatchTopic_harness 6 | 7 | # This should be a unique identifier for this proof, and will appear on the 8 | # Litani dashboard. It can be human-readable and contain spaces if you wish. 9 | PROOF_UID = FleetProvisioning_MatchTopic 10 | 11 | # The topic length is bounded to reduce the proof run time. Memory safety on the 12 | # buffer holding the topic string can be proven within a reasonable bound. It 13 | # adds no value to the proof to input the largest possible topic string accepted 14 | # by AWS (64KB). 15 | TOPIC_STRING_LENGTH_MAX=200 16 | 17 | DEFINES += -DTOPIC_STRING_LENGTH_MAX=$(TOPIC_STRING_LENGTH_MAX) 18 | INCLUDES += 19 | 20 | REMOVE_FUNCTION_BODY += 21 | 22 | # The longest strncmp is against FLEET_PROVISIONING_CREATE_CERTIFICATE_FROM_CSR_API_PREFIX 23 | # length of which is 35. We unwind one more time than the bridge length. 24 | FLEET_PROVISIONING_API_BRIDGE_LENGTH=36 25 | UNWINDSET += strncmp.0:$(FLEET_PROVISIONING_API_BRIDGE_LENGTH) 26 | 27 | # Enough to unwind the consumeTemplateName loop TOPIC_STRING_LENGTH_MAX times 28 | # as template name in the topic string can not be longer than the topic string 29 | # length. 30 | UNWINDSET += __CPROVER_file_local_fleet_provisioning_c_consumeTemplateName.0:$(TOPIC_STRING_LENGTH_MAX) 31 | 32 | PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c 33 | PROJECT_SOURCES += $(SRCDIR)/source/fleet_provisioning.c 34 | 35 | include ../Makefile.common 36 | -------------------------------------------------------------------------------- /test/cbmc/proofs/FleetProvisioning_MatchTopic/README.md: -------------------------------------------------------------------------------- 1 | FleetProvisioning_MatchTopic proof 2 | ============== 3 | 4 | This directory contains a memory safety proof for FleetProvisioning_MatchTopic. 5 | 6 | To run the proof. 7 | ------------- 8 | 9 | * Add `cbmc`, `goto-cc`, `goto-instrument`, `goto-analyzer`, and `cbmc-viewer` 10 | to your path. 11 | * Run `make`. 12 | * Open html/index.html in a web browser. 13 | 14 | To use [`arpa`](https://awslabs.github.io/aws-proof-build-assistant) to simplify writing Makefiles. 15 | ------------- 16 | 17 | * Run `make arpa` to generate a Makefile.arpa that contains relevant build information for the proof. 18 | * Use Makefile.arpa as the starting point for your proof Makefile by: 19 | 1. Modifying Makefile.arpa (if required). 20 | 2. Including Makefile.arpa into the existing proof Makefile (add `sinclude Makefile.arpa` at the bottom of the Makefile, right before `include ../Makefile.common`). 21 | -------------------------------------------------------------------------------- /test/cbmc/proofs/FleetProvisioning_MatchTopic/cbmc-proof.txt: -------------------------------------------------------------------------------- 1 | # This file marks this directory as containing a CBMC proof. 2 | -------------------------------------------------------------------------------- /test/cbmc/proofs/FleetProvisioning_MatchTopic/cbmc-viewer.json: -------------------------------------------------------------------------------- 1 | { "expected-missing-functions": 2 | [ 3 | 4 | ], 5 | "proof-name": "FleetProvisioning_MatchTopic", 6 | "proof-root": "test/cbmc/proofs" 7 | } 8 | -------------------------------------------------------------------------------- /test/cbmc/proofs/Makefile-project-defines: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # The first line sets the emacs major mode to Makefile 3 | 4 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | ################################################################ 8 | # Use this file to give project-specific definitions of the command 9 | # line arguments to pass to CBMC tools like goto-cc to build the goto 10 | # binaries and cbmc to do the property and coverage checking. 11 | # 12 | # Use this file to override most default definitions of variables in 13 | # Makefile.common. 14 | ################################################################ 15 | 16 | # Flags to pass to goto-cc for compilation (typically those passed to gcc -c) 17 | COMPILE_FLAGS += -fPIC -std=gnu90 18 | 19 | # Flags to pass to goto-cc for linking (typically those passed to gcc) 20 | # LINK_FLAGS = 21 | 22 | # Preprocessor include paths -I... 23 | # Consider adding 24 | # INCLUDES += -I$(CBMC_ROOT)/include 25 | # You will want to decide what order that comes in relative to the other 26 | # include directories in your project. 27 | # 28 | INCLUDES += -I$(CBMC_ROOT)/include 29 | INCLUDES += -I$(SRCDIR)/source/include 30 | INCLUDES += -I$(SRCDIR)/test/include 31 | 32 | # Preprocessor definitions -D... 33 | # DEFINES = 34 | 35 | # Path to arpa executable 36 | # ARPA = 37 | 38 | # Flags to pass to cmake for building the project 39 | # ARPA_CMAKE_FLAGS = 40 | -------------------------------------------------------------------------------- /test/cbmc/proofs/Makefile-project-targets: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # The first line sets the emacs major mode to Makefile 3 | 4 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | ################################################################ 8 | # Use this file to give project-specific targets, including targets 9 | # that may depend on targets defined in Makefile.common. 10 | ################################################################ 11 | -------------------------------------------------------------------------------- /test/cbmc/proofs/Makefile-project-testing: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # The first line sets the emacs major mode to Makefile 3 | 4 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | ################################################################ 8 | # Use this file to define project-specific targets and definitions for 9 | # unit testing or continuous integration that may depend on targets 10 | # defined in Makefile.common 11 | ################################################################ 12 | -------------------------------------------------------------------------------- /test/cbmc/proofs/Makefile-template-defines: -------------------------------------------------------------------------------- 1 | 2 | # Absolute path to the root of the source tree. 3 | # 4 | SRCDIR ?= $(abspath $(PROOF_ROOT)/../../..) 5 | 6 | 7 | # Absolute path to the litani script. 8 | # 9 | LITANI ?= litani 10 | 11 | 12 | # Name of this proof project, displayed in proof reports. For example, 13 | # "s2n" or "Amazon FreeRTOS". For projects with multiple proof roots, 14 | # this may be overridden on the command-line to Make, for example 15 | # 16 | # make PROJECT_NAME="FreeRTOS MQTT" report 17 | # 18 | PROJECT_NAME = "Fleet-Provisioning-for-AWS-IoT-embedded-sdk" 19 | 20 | -------------------------------------------------------------------------------- /test/cbmc/proofs/Makefile.common: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | # The first line sets the emacs major mode to Makefile 3 | 4 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | # SPDX-License-Identifier: MIT-0 6 | 7 | CBMC_STARTER_KIT_VERSION = CBMC starter kit 2.11 8 | 9 | ################################################################ 10 | # The CBMC Starter Kit depends on the files Makefile.common and 11 | # run-cbmc-proofs.py. They are installed by the setup script 12 | # cbmc-starter-kit-setup and updated to the latest version by the 13 | # update script cbmc-starter-kit-update. For more information about 14 | # the starter kit and these files and these scripts, see 15 | # https://model-checking.github.io/cbmc-starter-kit 16 | # 17 | # Makefile.common implements what we consider to be some best 18 | # practices for using cbmc for software verification. 19 | # 20 | # Section I gives default values for a large number of Makefile 21 | # variables that control 22 | # * how your code is built (include paths, etc), 23 | # * what program transformations are applied to your code (loop 24 | # unwinding, etc), and 25 | # * what properties cbmc checks for in your code (memory safety, etc). 26 | # 27 | # These variables are defined below with definitions of the form 28 | # VARIABLE ?= DEFAULT_VALUE 29 | # meaning VARIABLE is set to DEFAULT_VALUE if VARIABLE has not already 30 | # been given a value. 31 | # 32 | # For your project, you can override these default values with 33 | # project-specific definitions in Makefile-project-defines. 34 | # 35 | # For any individual proof, you can override these default values and 36 | # project-specific values with proof-specific definitions in the 37 | # Makefile for your proof. 38 | # 39 | # The definitions in the proof Makefile override definitions in the 40 | # project Makefile-project-defines which override definitions in this 41 | # Makefile.common. 42 | # 43 | # Section II uses the values defined in Section I to build your code, run 44 | # your proof, and build a report of your results. You should not need 45 | # to modify or override anything in Section II, but you may want to 46 | # read it to understand how the values defined in Section I control 47 | # things. 48 | # 49 | # To use Makefile.common, set variables as described above as needed, 50 | # and then for each proof, 51 | # 52 | # * Create a subdirectory . 53 | # * Write a proof harness (a function) with the name 54 | # in a file with the name /.c 55 | # * Write a makefile with the name /Makefile that looks 56 | # something like 57 | # 58 | # HARNESS_FILE= 59 | # HARNESS_ENTRY= 60 | # PROOF_UID= 61 | # 62 | # PROJECT_SOURCES += $(SRCDIR)/libraries/api_1.c 63 | # PROJECT_SOURCES += $(SRCDIR)/libraries/api_2.c 64 | # 65 | # PROOF_SOURCES += $(PROOFDIR)/harness.c 66 | # PROOF_SOURCES += $(SRCDIR)/cbmc/proofs/stub_a.c 67 | # PROOF_SOURCES += $(SRCDIR)/cbmc/proofs/stub_b.c 68 | # 69 | # UNWINDSET += foo.0:3 70 | # UNWINDSET += bar.1:6 71 | # 72 | # REMOVE_FUNCTION_BODY += api_stub_a 73 | # REMOVE_FUNCTION_BODY += api_stub_b 74 | # 75 | # DEFINES = -DDEBUG=0 76 | # 77 | # include ../Makefile.common 78 | # 79 | # * Change directory to and run make 80 | # 81 | # The proof setup script cbmc-starter-kit-setup-proof from the CBMC 82 | # Starter Kit will do most of this for, creating a directory and 83 | # writing a basic Makefile and proof harness into it that you can edit 84 | # as described above. 85 | # 86 | # Warning: If you get results that are hard to explain, consider 87 | # running "make clean" or "make veryclean" before "make" if you get 88 | # results that are hard to explain. Dependency handling in this 89 | # Makefile.common may not be perfect. 90 | 91 | SHELL=/bin/bash 92 | 93 | default: report 94 | 95 | ################################################################ 96 | ################################################################ 97 | ## Section I: This section gives common variable definitions. 98 | ## 99 | ## Override these definitions in Makefile-project-defines or 100 | ## your proof Makefile. 101 | ## 102 | ## Remember that Makefile.common and Makefile-project-defines are 103 | ## included into the proof Makefile in your proof directory, so all 104 | ## relative pathnames defined there should be relative to your proof 105 | ## directory. 106 | 107 | ################################################################ 108 | # Define the layout of the source tree and the proof subtree 109 | # 110 | # Generally speaking, 111 | # 112 | # SRCDIR = the root of the repository 113 | # CBMC_ROOT = /srcdir/cbmc 114 | # PROOF_ROOT = /srcdir/cbmc/proofs 115 | # PROOF_SOURCE = /srcdir/cbmc/sources 116 | # PROOF_INCLUDE = /srcdir/cbmc/include 117 | # PROOF_STUB = /srcdir/cbmc/stubs 118 | # PROOFDIR = the directory containing the Makefile for your proof 119 | # 120 | # The path /srcdir/cbmc used in the example above is determined by the 121 | # setup script cbmc-starter-kit-setup. Projects usually create a cbmc 122 | # directory somewhere in the source tree, and run the setup script in 123 | # that directory. The value of CBMC_ROOT becomes the absolute path to 124 | # that directory. 125 | # 126 | # The location of that cbmc directory in the source tree affects the 127 | # definition of SRCDIR, which is defined in terms of the relative path 128 | # from a proof directory to the repository root. The definition is 129 | # usually determined by the setup script cbmc-starter-kit-setup and 130 | # written to Makefile-template-defines, but you can override it for a 131 | # project in Makefile-project-defines and for a specific proof in the 132 | # Makefile for the proof. 133 | 134 | # Absolute path to the directory containing this Makefile.common 135 | # See https://ftp.gnu.org/old-gnu/Manuals/make-3.80/html_node/make_17.html 136 | # 137 | # Note: We compute the absolute paths to the makefiles in MAKEFILE_LIST 138 | # before we filter the list of makefiles for %/Makefile.common. 139 | # Otherwise an invocation of the form "make -f Makefile.common" will set 140 | # MAKEFILE_LIST to "Makefile.common" which will fail to match the 141 | # pattern %/Makefile.common. 142 | # 143 | MAKEFILE_PATHS = $(foreach makefile,$(MAKEFILE_LIST),$(abspath $(makefile))) 144 | PROOF_ROOT = $(dir $(filter %/Makefile.common,$(MAKEFILE_PATHS))) 145 | 146 | CBMC_ROOT = $(shell dirname $(PROOF_ROOT)) 147 | PROOF_SOURCE = $(CBMC_ROOT)/sources 148 | PROOF_INCLUDE = $(CBMC_ROOT)/include 149 | PROOF_STUB = $(CBMC_ROOT)/stubs 150 | 151 | # Project-specific definitions to override default definitions below 152 | # * Makefile-project-defines will never be overwritten 153 | # * Makefile-template-defines may be overwritten when the starter 154 | # kit is updated 155 | sinclude $(PROOF_ROOT)/Makefile-project-defines 156 | sinclude $(PROOF_ROOT)/Makefile-template-defines 157 | 158 | # SRCDIR is the path to the root of the source tree 159 | # This is a default definition that is frequently overridden in 160 | # another Makefile, see the discussion of SRCDIR above. 161 | SRCDIR ?= $(abspath ../..) 162 | 163 | # PROOFDIR is the path to the directory containing the proof harness 164 | PROOFDIR ?= $(abspath .) 165 | 166 | ################################################################ 167 | # Define how to run CBMC 168 | 169 | # Do property checking with the external SAT solver given by 170 | # EXTERNAL_SAT_SOLVER. Do coverage checking with the default solver, 171 | # since coverage checking requires the use of an incremental solver. 172 | # The EXTERNAL_SAT_SOLVER variable is typically set (if it is at all) 173 | # as an environment variable or as a makefile variable in 174 | # Makefile-project-defines. 175 | # 176 | # For a particular proof, if the default solver is faster, do property 177 | # checking with the default solver by including this definition in the 178 | # proof Makefile: 179 | # USE_EXTERNAL_SAT_SOLVER = 180 | # 181 | ifneq ($(strip $(EXTERNAL_SAT_SOLVER)),) 182 | USE_EXTERNAL_SAT_SOLVER ?= --external-sat-solver $(EXTERNAL_SAT_SOLVER) 183 | endif 184 | CHECKFLAGS += $(USE_EXTERNAL_SAT_SOLVER) 185 | 186 | # Job pools 187 | # For version of Litani that are new enough (where `litani print-capabilities` 188 | # prints "pools"), proofs for which `EXPENSIVE = true` is set can be added to a 189 | # "job pool" that restricts how many expensive proofs are run at a time. All 190 | # other proofs will be built in parallel as usual. 191 | # 192 | # In more detail: all compilation, instrumentation, and report jobs are run with 193 | # full parallelism as usual, even for expensive proofs. The CBMC jobs for 194 | # non-expensive proofs are also run in parallel. The only difference is that the 195 | # CBMC safety checks and coverage checks for expensive proofs are run with a 196 | # restricted parallelism level. At any one time, only N of these jobs are run at 197 | # once, amongst all the proofs. 198 | # 199 | # To configure N, Litani needs to be initialized with a pool called "expensive". 200 | # For example, to only run two CBMC safety/coverage jobs at a time from amongst 201 | # all the proofs, you would initialize litani like 202 | # litani init --pools expensive:2 203 | # The run-cbmc-proofs.py script takes care of this initialization through the 204 | # --expensive-jobs-parallelism flag. 205 | # 206 | # To enable this feature, set 207 | # the ENABLE_POOLS variable when running Make, like 208 | # `make ENABLE_POOLS=true report` 209 | # The run-cbmc-proofs.py script takes care of this through the 210 | # --restrict-expensive-jobs flag. 211 | 212 | ifeq ($(strip $(ENABLE_POOLS)),) 213 | POOL = 214 | INIT_POOLS = 215 | else ifeq ($(strip $(EXPENSIVE)),) 216 | POOL = 217 | INIT_POOLS = 218 | else 219 | POOL = --pool expensive 220 | INIT_POOLS = --pools expensive:1 221 | endif 222 | 223 | # Similar to the pool feature above. If Litani is new enough, enable 224 | # profiling CBMC's memory use. 225 | ifeq ($(strip $(ENABLE_MEMORY_PROFILING)),) 226 | MEMORY_PROFILING = 227 | else 228 | MEMORY_PROFILING = --profile-memory 229 | endif 230 | 231 | # Property checking flags 232 | # 233 | # Each variable below controls a specific property checking flag 234 | # within CBMC. If desired, a property flag can be disabled within 235 | # a particular proof by nulling the corresponding variable when CBMC's default 236 | # is not to perform such checks, or setting to --no--check when CBMC's 237 | # default is to perform such checks. For instance, the following lines: 238 | # 239 | # CBMC_FLAG_POINTER_CHECK = --no-pointer-check 240 | # CBMC_FLAG_UNSIGNED_OVERFLOW_CHECK = 241 | # 242 | # would disable pointer checks and unsigned overflow checks with CBMC flag 243 | # within: 244 | # * an entire project when added to Makefile-project-defines 245 | # * a specific proof when added to the harness Makefile 246 | 247 | CBMC_FLAG_MALLOC_MAY_FAIL ?= # set to --no-malloc-may-fail to disable 248 | CBMC_FLAG_BOUNDS_CHECK ?= # set to --no-bounds-check to disable 249 | CBMC_FLAG_CONVERSION_CHECK ?= --conversion-check 250 | CBMC_FLAG_DIV_BY_ZERO_CHECK ?= # set to --no-div-by-zero-check to disable 251 | CBMC_FLAG_FLOAT_OVERFLOW_CHECK ?= --float-overflow-check 252 | CBMC_FLAG_NAN_CHECK ?= --nan-check 253 | CBMC_FLAG_POINTER_CHECK ?= #set to --no-pointer-check to disable 254 | CBMC_FLAG_POINTER_OVERFLOW_CHECK ?= --pointer-overflow-check 255 | CBMC_FLAG_POINTER_PRIMITIVE_CHECK ?= # set to --no-pointer-primitive-check to disable 256 | CBMC_FLAG_SIGNED_OVERFLOW_CHECK ?= # set to --no-signed-overflow-check to disable 257 | CBMC_FLAG_UNDEFINED_SHIFT_CHECK ?= # set to --no-undefined-shift-check to disable 258 | CBMC_FLAG_UNSIGNED_OVERFLOW_CHECK ?= --unsigned-overflow-check 259 | CBMC_FLAG_UNWINDING_ASSERTIONS ?= # set to --no-unwinding-assertions to disable 260 | CBMC_DEFAULT_UNWIND ?= --unwind 1 261 | CBMC_FLAG_FLUSH ?= --flush 262 | 263 | # CBMC flags used for property checking and coverage checking 264 | 265 | CBMCFLAGS += $(CBMC_FLAG_FLUSH) 266 | 267 | # CBMC 6.0.0 enables all standard checks by default, which can make coverage analysis 268 | # very slow. See https://github.com/diffblue/cbmc/issues/8389 269 | # For now, we disable these checks when generating coverage info. 270 | COVERFLAGS ?= --no-standard-checks --malloc-may-fail --malloc-fail-null 271 | 272 | # CBMC flags used for property checking 273 | 274 | CHECKFLAGS += $(CBMC_FLAG_BOUNDS_CHECK) 275 | CHECKFLAGS += $(CBMC_FLAG_CONVERSION_CHECK) 276 | CHECKFLAGS += $(CBMC_FLAG_DIV_BY_ZERO_CHECK) 277 | CHECKFLAGS += $(CBMC_FLAG_FLOAT_OVERFLOW_CHECK) 278 | CHECKFLAGS += $(CBMC_FLAG_NAN_CHECK) 279 | CHECKFLAGS += $(CBMC_FLAG_POINTER_CHECK) 280 | CHECKFLAGS += $(CBMC_FLAG_POINTER_OVERFLOW_CHECK) 281 | CHECKFLAGS += $(CBMC_FLAG_POINTER_PRIMITIVE_CHECK) 282 | CHECKFLAGS += $(CBMC_FLAG_SIGNED_OVERFLOW_CHECK) 283 | CHECKFLAGS += $(CBMC_FLAG_UNDEFINED_SHIFT_CHECK) 284 | CHECKFLAGS += $(CBMC_FLAG_UNSIGNED_OVERFLOW_CHECK) 285 | 286 | # Additional CBMC flag to CBMC control verbosity. 287 | # 288 | # Meaningful values are 289 | # 0 none 290 | # 1 only errors 291 | # 2 + warnings 292 | # 4 + results 293 | # 6 + status/phase information 294 | # 8 + statistical information 295 | # 9 + progress information 296 | # 10 + debug info 297 | # 298 | # Uncomment the following line or set in Makefile-project-defines 299 | # CBMC_VERBOSITY ?= --verbosity 4 300 | 301 | # Additional CBMC flag to control how CBMC treats static variables. 302 | # 303 | # NONDET_STATIC is a list of flags of the form --nondet-static 304 | # and --nondet-static-exclude VAR. The --nondet-static flag causes 305 | # CBMC to initialize static variables with unconstrained value 306 | # (ignoring initializers and default zero-initialization). The 307 | # --nondet-static-exclude VAR excludes VAR for the variables 308 | # initialized with unconstrained values. 309 | NONDET_STATIC ?= 310 | 311 | # Flags to pass to goto-cc for compilation and linking 312 | COMPILE_FLAGS ?= -Wall -Werror 313 | LINK_FLAGS ?= -Wall -Werror 314 | EXPORT_FILE_LOCAL_SYMBOLS ?= --export-file-local-symbols 315 | 316 | # During instrumentation, it adds models of C library functions 317 | ADD_LIBRARY_FLAG := --add-library 318 | 319 | # Preprocessor include paths -I... 320 | INCLUDES ?= 321 | 322 | # Preprocessor definitions -D... 323 | DEFINES ?= 324 | 325 | # CBMC object model 326 | # 327 | # CBMC_OBJECT_BITS is the number of bits in a pointer CBMC uses for 328 | # the id of the object to which a pointer is pointing. CBMC uses 8 329 | # bits for the object id by default. The remaining bits in the pointer 330 | # are used for offset into the object. This limits the size of the 331 | # objects that CBMC can model. This Makefile defines this bound on 332 | # object size to be CBMC_MAX_OBJECT_SIZE. You are likely to get 333 | # unexpected results if you try to malloc an object larger than this 334 | # bound. 335 | CBMC_OBJECT_BITS ?= 8 336 | 337 | # CBMC loop unwinding (Normally set in the proof Makefile) 338 | # 339 | # UNWINDSET is a list of pairs of the form foo.1:4 meaning that 340 | # CBMC should unwind loop 1 in function foo no more than 4 times. 341 | # For historical reasons, the number 4 is one more than the number 342 | # of times CBMC actually unwinds the loop. 343 | UNWINDSET ?= 344 | 345 | # CBMC early loop unwinding (Normally set in the proof Makefile) 346 | # 347 | # Most users can ignore this variable. 348 | # 349 | # This variable exists to support the use of loop and function 350 | # contracts, two features under development for CBMC. Checking the 351 | # assigns clause for function contracts and loop invariants currently 352 | # assumes loop-free bodies for loops and functions with contracts 353 | # (possibly after replacing nested loops with their own loop 354 | # contracts). To satisfy this requirement, it may be necessary to 355 | # unwind some loops before the function contract and loop invariant 356 | # transformations are applied to the goto program. This variable 357 | # CPROVER_LIBRARY_UNWINDSET is identical to UNWINDSET, and we assume that the 358 | # loops mentioned in CPROVER_LIBRARY_UNWINDSET and UNWINDSET are disjoint. 359 | CPROVER_LIBRARY_UNWINDSET ?= 360 | 361 | # CBMC function removal (Normally set set in the proof Makefile) 362 | # 363 | # REMOVE_FUNCTION_BODY is a list of function names. CBMC will "undefine" 364 | # the function, and CBMC will treat the function as having no side effects 365 | # and returning an unconstrained value of the appropriate return type. 366 | # The list should include the names of functions being stubbed out. 367 | REMOVE_FUNCTION_BODY ?= 368 | 369 | # CBMC function pointer restriction (Normally set in the proof Makefile) 370 | # 371 | # RESTRICT_FUNCTION_POINTER is a list of function pointer restriction 372 | # instructions of the form: 373 | # 374 | # .function_pointer_call./[,]* 375 | # 376 | # The function pointer call number in the specified function gets 377 | # rewritten to a case switch over a finite list of functions. 378 | # If some possible target functions are omitted from the list a counter 379 | # example trace will be found by CBMC, i.e. the transformation is sound. 380 | # If the target functions are file-local symbols, then mangled names must 381 | # be used. 382 | RESTRICT_FUNCTION_POINTER ?= 383 | 384 | # The project source files (Normally set set in the proof Makefile) 385 | # 386 | # PROJECT_SOURCES is the list of project source files to compile, 387 | # including the source file defining the function under test. 388 | PROJECT_SOURCES ?= 389 | 390 | # The proof source files (Normally set in the proof Makefile) 391 | # 392 | # PROOF_SOURCES is the list of proof source files to compile, including 393 | # the proof harness, and including any function stubs being used. 394 | PROOF_SOURCES ?= 395 | 396 | # The number of seconds that CBMC should be allowed to run for before 397 | # being forcefully terminated. Currently, this is set to be less than 398 | # the time limit for a CodeBuild job, which is eight hours. If a proof 399 | # run takes longer than the time limit of the CI environment, the 400 | # environment will halt the proof run without updating the Litani 401 | # report, making the proof run appear to "hang". 402 | CBMC_TIMEOUT ?= 21600 403 | 404 | # CBMC string abstraction 405 | # 406 | # Replace all uses of char * by a struct that carries that string, 407 | # and also the underlying allocation and the C string length. 408 | STRING_ABSTRACTION ?= 409 | ifdef STRING_ABSTRACTION 410 | ifneq ($(strip $(STRING_ABSTRACTION)),) 411 | CBMC_STRING_ABSTRACTION := --string-abstraction 412 | endif 413 | endif 414 | 415 | # Optional configuration library flags 416 | OPT_CONFIG_LIBRARY ?= 417 | CBMC_OPT_CONFIG_LIBRARY := $(CBMC_FLAG_MALLOC_MAY_FAIL) $(CBMC_STRING_ABSTRACTION) 418 | 419 | # Proof writers could add function contracts in their source code. 420 | # These contracts are ignored by default, but may be enabled in two distinct 421 | # contexts using the following two variables: 422 | # 1. To check whether one or more function contracts are sound with respect to 423 | # the function implementation, CHECK_FUNCTION_CONTRACTS should be a list of 424 | # function names. Use CHECK_FUNCTION_CONTRACTS_REC to check contracts on 425 | # recursive functions. 426 | # 2. To replace calls to certain functions with their correspondent function 427 | # contracts, USE_FUNCTION_CONTRACTS should be a list of function names. 428 | # One must check separately whether a function contract is sound before 429 | # replacing it in calling contexts. 430 | CHECK_FUNCTION_CONTRACTS ?= 431 | CBMC_CHECK_FUNCTION_CONTRACTS := $(patsubst %,--enforce-contract %, $(CHECK_FUNCTION_CONTRACTS)) 432 | 433 | CHECK_FUNCTION_CONTRACTS_REC ?= 434 | CBMC_CHECK_FUNCTION_CONTRACTS_REC := $(patsubst %,--enforce-contract-rec %, $(CHECK_FUNCTION_CONTRACTS_REC)) 435 | 436 | USE_FUNCTION_CONTRACTS ?= 437 | CBMC_USE_FUNCTION_CONTRACTS := $(patsubst %,--replace-call-with-contract %, $(USE_FUNCTION_CONTRACTS)) 438 | 439 | CODE_CONTRACTS := $(CHECK_FUNCTION_CONTRACTS)$(USE_FUNCTION_CONTRACTS)$(APPLY_LOOP_CONTRACTS) 440 | 441 | # Proof writers may also apply function contracts using the Dynamic Frame 442 | # Condition Checking (DFCC) mode. For more information on DFCC, 443 | # please see https://diffblue.github.io/cbmc/contracts-dev-spec-dfcc.html. 444 | USE_DYNAMIC_FRAMES ?= 445 | ifdef USE_DYNAMIC_FRAMES 446 | ifneq ($(strip $(USE_DYNAMIC_FRAMES)),) 447 | CBMC_USE_DYNAMIC_FRAMES := $(CBMC_OPT_CONFIG_LIBRARY) --dfcc $(HARNESS_ENTRY) $(CBMC_CHECK_FUNCTION_CONTRACTS_REC) 448 | endif 449 | endif 450 | 451 | # Similarly, proof writers could also add loop contracts in their source code 452 | # to obtain unbounded correctness proofs. Unlike function contracts, loop 453 | # contracts are not reusable and thus are checked and used simultaneously. 454 | # These contracts are also ignored by default, but may be enabled by setting 455 | # the APPLY_LOOP_CONTRACTS variable. 456 | APPLY_LOOP_CONTRACTS ?= 457 | ifdef APPLY_LOOP_CONTRACTS 458 | ifneq ($(strip $(APPLY_LOOP_CONTRACTS)),) 459 | CBMC_APPLY_LOOP_CONTRACTS := --apply-loop-contracts 460 | endif 461 | endif 462 | 463 | # The default unwind should only be used in DFCC mode without loop contracts. 464 | # When loop contracts are applied, we only unwind specified loops. 465 | # If any loops remain after loop contracts have been applied, CBMC might try 466 | # to unwind the program indefinitely, because we do not pass default unwind 467 | # (i.e., --unwind 1) to CBMC when in DFCC mode. 468 | # We must not use a default unwind command in DFCC mode, because contract instrumentation 469 | # introduces loops encoding write set inclusion checks that must be dynamically unwound during 470 | # symex. 471 | ifneq ($(strip $(USE_DYNAMIC_FRAMES)),) 472 | ifneq ($(strip $(APPLY_LOOP_CONTRACTS)),) 473 | UNWIND_0500_FLAGS=$(CBMC_UNWINDSET) $(CBMC_CPROVER_LIBRARY_UNWINDSET) $(CBMC_FLAG_UNWINDING_ASSERTIONS) 474 | UNWIND_0500_DESC="$(PROOF_UID): unwinding specified subset of loops" 475 | else 476 | UNWIND_0500_FLAGS=$(CBMC_UNWINDSET) $(CBMC_CPROVER_LIBRARY_UNWINDSET) $(CBMC_DEFAULT_UNWIND) $(CBMC_FLAG_UNWINDING_ASSERTIONS) 477 | UNWIND_0500_DESC="$(PROOF_UID): unwinding all loops" 478 | endif 479 | endif 480 | 481 | # Silence makefile output (eg, long litani commands) unless VERBOSE is set. 482 | ifndef VERBOSE 483 | MAKEFLAGS := $(MAKEFLAGS) -s 484 | endif 485 | 486 | ################################################################ 487 | ################################################################ 488 | ## Section II: This section defines the process of running a proof 489 | ## 490 | ## There should be no reason to edit anything below this line. 491 | 492 | ################################################################ 493 | # Paths 494 | 495 | CBMC ?= cbmc 496 | GOTO_ANALYZER ?= goto-analyzer 497 | GOTO_CC ?= goto-cc 498 | GOTO_INSTRUMENT ?= goto-instrument 499 | CRANGLER ?= crangler 500 | VIEWER ?= cbmc-viewer 501 | VIEWER2 ?= cbmc-viewer 502 | CMAKE ?= cmake 503 | 504 | GOTODIR ?= $(PROOFDIR)/gotos 505 | LOGDIR ?= $(PROOFDIR)/logs 506 | 507 | PROJECT ?= project 508 | PROOF ?= proof 509 | 510 | HARNESS_GOTO ?= $(GOTODIR)/$(HARNESS_FILE) 511 | PROJECT_GOTO ?= $(GOTODIR)/$(PROJECT) 512 | PROOF_GOTO ?= $(GOTODIR)/$(PROOF) 513 | 514 | ################################################################ 515 | # Useful macros for values that are hard to reference 516 | SPACE :=$() $() 517 | COMMA :=, 518 | 519 | ################################################################ 520 | # Set C compiler defines 521 | 522 | CBMCFLAGS += --object-bits $(CBMC_OBJECT_BITS) 523 | 524 | DEFINES += -DCBMC=1 525 | DEFINES += -DCBMC_OBJECT_BITS=$(CBMC_OBJECT_BITS) 526 | DEFINES += -DCBMC_MAX_OBJECT_SIZE="(SIZE_MAX>>(CBMC_OBJECT_BITS+1))" 527 | 528 | # CI currently assumes cbmc invocation has at most one --unwindset 529 | 530 | # UNWINDSET is designed for user code (i.e., proof and project code) 531 | ifdef UNWINDSET 532 | ifneq ($(strip $(UNWINDSET)),) 533 | CBMC_UNWINDSET := --unwindset $(subst $(SPACE),$(COMMA),$(strip $(UNWINDSET))) 534 | endif 535 | endif 536 | 537 | # CPROVER_LIBRARY_UNWINDSET is designed for CPROVER library functions 538 | ifdef CPROVER_LIBRARY_UNWINDSET 539 | ifneq ($(strip $(CPROVER_LIBRARY_UNWINDSET)),) 540 | CBMC_CPROVER_LIBRARY_UNWINDSET := --unwindset $(subst $(SPACE),$(COMMA),$(strip $(CPROVER_LIBRARY_UNWINDSET))) 541 | endif 542 | endif 543 | 544 | CBMC_REMOVE_FUNCTION_BODY := $(patsubst %,--remove-function-body %, $(REMOVE_FUNCTION_BODY)) 545 | 546 | ifdef RESTRICT_FUNCTION_POINTER 547 | ifneq ($(strip $(RESTRICT_FUNCTION_POINTER)),) 548 | CBMC_RESTRICT_FUNCTION_POINTER := $(patsubst %,--restrict-function-pointer %, $(RESTRICT_FUNCTION_POINTER)) 549 | endif 550 | endif 551 | 552 | ################################################################ 553 | # Targets for rewriting source files with crangler 554 | 555 | # Construct crangler configuration files 556 | # 557 | # REWRITTEN_SOURCES is a list of crangler output files source.i. 558 | # This target assumes that for each source.i 559 | # * source.i_SOURCE is the path to a source file, 560 | # * source.i_FUNCTIONS is a list of functions (may be empty) 561 | # * source.i_OBJECTS is a list of variables (may be empty) 562 | # This target constructs the crangler configuration file source.i.json 563 | # of the form 564 | # { 565 | # "sources": [ "/proj/code.c" ], 566 | # "includes": [ "/proj/include" ], 567 | # "defines": [ "VAR=1" ], 568 | # "functions": [ {"function_name": ["remove static"]} ], 569 | # "objects": [ {"variable_name": ["remove static"]} ], 570 | # "output": "source.i" 571 | # } 572 | # to remove the static attribute from function_name and variable_name 573 | # in the source file source.c and write the result to source.i. 574 | # 575 | # This target assumes that filenames include no spaces and that 576 | # the INCLUDES and DEFINES variables include no spaces after -I 577 | # and -D. For example, use "-DVAR=1" and not "-D VAR=1". 578 | # 579 | # Define *_SOURCE, *_FUNCTIONS, and *_OBJECTS in the proof Makefile. 580 | # The string source.i is usually an absolute path $(PROOFDIR)/code.i 581 | # to a file in the proof directory that contains the proof Makefile. 582 | # The proof Makefile usually includes the definitions 583 | # $(PROOFDIR)/code.i_SOURCE = /proj/code.c 584 | # $(PROOFDIR)/code.i_FUNCTIONS = function_name 585 | # $(PROOFDIR)/code.i_OBJECTS = variable_name 586 | # Because these definitions refer to PROOFDIR that is defined in this 587 | # Makefile.common, these definitions must appear after the inclusion 588 | # of Makefile.common in the proof Makefile. 589 | # 590 | $(foreach rs,$(REWRITTEN_SOURCES),$(eval $(rs).json: $($(rs)_SOURCE))) 591 | $(foreach rs,$(REWRITTEN_SOURCES),$(rs).json): 592 | echo '{'\ 593 | '"sources": ['\ 594 | '"$($(@:.json=)_SOURCE)"'\ 595 | '],'\ 596 | '"includes": ['\ 597 | '$(subst $(SPACE),$(COMMA),$(patsubst -I%,"%",$(strip $(INCLUDES))))' \ 598 | '],'\ 599 | '"defines": ['\ 600 | '$(subst $(SPACE),$(COMMA),$(patsubst -D%,"%",$(subst ",\",$(strip $(DEFINES)))))' \ 601 | '],'\ 602 | '"functions": ['\ 603 | '{'\ 604 | '$(subst ~, ,$(subst $(SPACE),$(COMMA),$(patsubst %,"%":["remove~static"],$($(@:.json=)_FUNCTIONS))))' \ 605 | '}'\ 606 | '],'\ 607 | '"objects": ['\ 608 | '{'\ 609 | '$(subst ~, ,$(subst $(SPACE),$(COMMA),$(patsubst %,"%":["remove~static"],$($(@:.json=)_OBJECTS))))' \ 610 | '}'\ 611 | '],'\ 612 | '"output": "$(@:.json=)"'\ 613 | '}' > $@ 614 | 615 | # Rewrite source files with crangler 616 | # 617 | $(foreach rs,$(REWRITTEN_SOURCES),$(eval $(rs): $(rs).json)) 618 | $(REWRITTEN_SOURCES): 619 | $(LITANI) add-job \ 620 | --command \ 621 | '$(CRANGLER) $@.json' \ 622 | --inputs $($@_SOURCE) \ 623 | --outputs $@ \ 624 | --stdout-file $(LOGDIR)/crangler-$(subst /,_,$(subst .,_,$@))-log.txt \ 625 | --interleave-stdout-stderr \ 626 | --pipeline-name "$(PROOF_UID)" \ 627 | --ci-stage build \ 628 | --description "$(PROOF_UID): removing static" 629 | 630 | ################################################################ 631 | # Build targets that make the relevant .goto files 632 | 633 | # Compile project sources 634 | $(PROJECT_GOTO)0100.goto: $(PROJECT_SOURCES) $(REWRITTEN_SOURCES) 635 | $(LITANI) add-job \ 636 | --command \ 637 | '$(GOTO_CC) $(CBMC_VERBOSITY) $(COMPILE_FLAGS) $(EXPORT_FILE_LOCAL_SYMBOLS) $(INCLUDES) $(DEFINES) $^ -o $@' \ 638 | --inputs $^ \ 639 | --outputs $@ \ 640 | --stdout-file $(LOGDIR)/project_sources-log.txt \ 641 | --pipeline-name "$(PROOF_UID)" \ 642 | --ci-stage build \ 643 | --description "$(PROOF_UID): building project binary" 644 | 645 | # Compile proof sources 646 | $(PROOF_GOTO)0100.goto: $(PROOF_SOURCES) 647 | $(LITANI) add-job \ 648 | --command \ 649 | '$(GOTO_CC) $(CBMC_VERBOSITY) $(COMPILE_FLAGS) $(EXPORT_FILE_LOCAL_SYMBOLS) $(INCLUDES) $(DEFINES) $^ -o $@' \ 650 | --inputs $^ \ 651 | --outputs $@ \ 652 | --stdout-file $(LOGDIR)/proof_sources-log.txt \ 653 | --pipeline-name "$(PROOF_UID)" \ 654 | --ci-stage build \ 655 | --description "$(PROOF_UID): building proof binary" 656 | 657 | # Remove function bodies from project sources 658 | $(PROJECT_GOTO)0200.goto: $(PROJECT_GOTO)0100.goto 659 | $(LITANI) add-job \ 660 | --command \ 661 | '$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_REMOVE_FUNCTION_BODY) $^ $@' \ 662 | --inputs $^ \ 663 | --outputs $@ \ 664 | --stdout-file $(LOGDIR)/remove_function_body-log.txt \ 665 | --pipeline-name "$(PROOF_UID)" \ 666 | --ci-stage build \ 667 | --description "$(PROOF_UID): removing function bodies from project sources" 668 | 669 | # Link project and proof sources into the proof harness 670 | $(HARNESS_GOTO)0100.goto: $(PROOF_GOTO)0100.goto $(PROJECT_GOTO)0200.goto 671 | $(LITANI) add-job \ 672 | --command '$(GOTO_CC) $(CBMC_VERBOSITY) --function $(HARNESS_ENTRY) $^ $(LINK_FLAGS) -o $@' \ 673 | --inputs $^ \ 674 | --outputs $@ \ 675 | --stdout-file $(LOGDIR)/link_proof_project-log.txt \ 676 | --pipeline-name "$(PROOF_UID)" \ 677 | --ci-stage build \ 678 | --description "$(PROOF_UID): linking project to proof" 679 | 680 | # Restrict function pointers 681 | $(HARNESS_GOTO)0200.goto: $(HARNESS_GOTO)0100.goto 682 | $(LITANI) add-job \ 683 | --command \ 684 | '$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_RESTRICT_FUNCTION_POINTER) --remove-function-pointers $^ $@' \ 685 | --inputs $^ \ 686 | --outputs $@ \ 687 | --stdout-file $(LOGDIR)/restrict_function_pointer-log.txt \ 688 | --pipeline-name "$(PROOF_UID)" \ 689 | --ci-stage build \ 690 | --description "$(PROOF_UID): restricting function pointers in project sources" 691 | 692 | # Fill static variable with unconstrained values 693 | $(HARNESS_GOTO)0300.goto: $(HARNESS_GOTO)0200.goto 694 | ifneq ($(strip $(CODE_CONTRACTS)),) 695 | $(LITANI) add-job \ 696 | --command 'cp $^ $@' \ 697 | --inputs $^ \ 698 | --outputs $@ \ 699 | --stdout-file $(LOGDIR)/nondet_static-log.txt \ 700 | --pipeline-name "$(PROOF_UID)" \ 701 | --ci-stage build \ 702 | --description "$(PROOF_UID): not setting static variables to nondet (will do during contract instrumentation)" 703 | else 704 | $(LITANI) add-job \ 705 | --command \ 706 | '$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(NONDET_STATIC) $^ $@' \ 707 | --inputs $^ \ 708 | --outputs $@ \ 709 | --stdout-file $(LOGDIR)/nondet_static-log.txt \ 710 | --pipeline-name "$(PROOF_UID)" \ 711 | --ci-stage build \ 712 | --description "$(PROOF_UID): setting static variables to nondet" 713 | endif 714 | 715 | # Link CPROVER library if DFCC mode is on 716 | $(HARNESS_GOTO)0400.goto: $(HARNESS_GOTO)0300.goto 717 | ifneq ($(strip $(USE_DYNAMIC_FRAMES)),) 718 | $(LITANI) add-job \ 719 | --command \ 720 | '$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(ADD_LIBRARY_FLAG) $(CBMC_OPT_CONFIG_LIBRARY) $^ $@' \ 721 | --inputs $^ \ 722 | --outputs $@ \ 723 | --stdout-file $(LOGDIR)/linking-library-models-log.txt \ 724 | --pipeline-name "$(PROOF_UID)" \ 725 | --ci-stage build \ 726 | --description "$(PROOF_UID): linking CPROVER library" 727 | else 728 | $(LITANI) add-job \ 729 | --command 'cp $^ $@' \ 730 | --inputs $^ \ 731 | --outputs $@ \ 732 | --stdout-file $(LOGDIR)/linking-library-models-log.txt \ 733 | --pipeline-name "$(PROOF_UID)" \ 734 | --ci-stage build \ 735 | --description "$(PROOF_UID): not linking CPROVER library" 736 | endif 737 | 738 | # Early unwind all loops on DFCC mode; otherwise, only unwind loops in proof and project code 739 | $(HARNESS_GOTO)0500.goto: $(HARNESS_GOTO)0400.goto 740 | ifneq ($(strip $(USE_DYNAMIC_FRAMES)),) 741 | $(LITANI) add-job \ 742 | --command \ 743 | '$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(UNWIND_0500_FLAGS) $^ $@' \ 744 | --inputs $^ \ 745 | --outputs $@ \ 746 | --stdout-file $(LOGDIR)/unwind_loops-log.txt \ 747 | --pipeline-name "$(PROOF_UID)" \ 748 | --ci-stage build \ 749 | --description $(UNWIND_0500_DESC) 750 | else ifneq ($(strip $(CODE_CONTRACTS)),) 751 | $(LITANI) add-job \ 752 | --command \ 753 | '$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_UNWINDSET) $(CBMC_FLAG_UNWINDING_ASSERTIONS) $^ $@' \ 754 | --inputs $^ \ 755 | --outputs $@ \ 756 | --stdout-file $(LOGDIR)/unwind_loops-log.txt \ 757 | --pipeline-name "$(PROOF_UID)" \ 758 | --ci-stage build \ 759 | --description "$(PROOF_UID): unwinding loops in proof and project code" 760 | else 761 | $(LITANI) add-job \ 762 | --command 'cp $^ $@' \ 763 | --inputs $^ \ 764 | --outputs $@ \ 765 | --stdout-file $(LOGDIR)/unwind_loops-log.txt \ 766 | --pipeline-name "$(PROOF_UID)" \ 767 | --ci-stage build \ 768 | --description "$(PROOF_UID): not unwinding loops" 769 | endif 770 | 771 | # Replace function contracts, check function contracts, instrument for loop contracts 772 | $(HARNESS_GOTO)0600.goto: $(HARNESS_GOTO)0500.goto 773 | $(LITANI) add-job \ 774 | --command \ 775 | '$(GOTO_INSTRUMENT) $(CBMC_USE_DYNAMIC_FRAMES) $(NONDET_STATIC) $(CBMC_VERBOSITY) $(CBMC_CHECK_FUNCTION_CONTRACTS) $(CBMC_USE_FUNCTION_CONTRACTS) $(CBMC_APPLY_LOOP_CONTRACTS) $^ $@' \ 776 | --inputs $^ \ 777 | --outputs $@ \ 778 | --stdout-file $(LOGDIR)/check_function_contracts-log.txt \ 779 | --pipeline-name "$(PROOF_UID)" \ 780 | --ci-stage build \ 781 | --description "$(PROOF_UID): checking function contracts" 782 | 783 | # Omit initialization of unused global variables (reduces problem size) 784 | $(HARNESS_GOTO)0700.goto: $(HARNESS_GOTO)0600.goto 785 | $(LITANI) add-job \ 786 | --command \ 787 | '$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) --slice-global-inits $^ $@' \ 788 | --inputs $^ \ 789 | --outputs $@ \ 790 | --stdout-file $(LOGDIR)/slice_global_inits-log.txt \ 791 | --pipeline-name "$(PROOF_UID)" \ 792 | --ci-stage build \ 793 | --description "$(PROOF_UID): slicing global initializations" 794 | 795 | # Omit unused functions (sharpens coverage calculations) 796 | $(HARNESS_GOTO)0800.goto: $(HARNESS_GOTO)0700.goto 797 | $(LITANI) add-job \ 798 | --command \ 799 | '$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) --drop-unused-functions $^ $@' \ 800 | --inputs $^ \ 801 | --outputs $@ \ 802 | --stdout-file $(LOGDIR)/drop_unused_functions-log.txt \ 803 | --pipeline-name "$(PROOF_UID)" \ 804 | --ci-stage build \ 805 | --description "$(PROOF_UID): dropping unused functions" 806 | 807 | # Final name for proof harness 808 | $(HARNESS_GOTO).goto: $(HARNESS_GOTO)0800.goto 809 | $(LITANI) add-job \ 810 | --command 'cp $< $@' \ 811 | --inputs $^ \ 812 | --outputs $@ \ 813 | --pipeline-name "$(PROOF_UID)" \ 814 | --ci-stage build \ 815 | --description "$(PROOF_UID): copying final goto-binary" 816 | 817 | ################################################################ 818 | # Targets to run the analysis commands 819 | 820 | ifdef CBMCFLAGS 821 | ifeq ($(strip $(CODE_CONTRACTS)),) 822 | CBMCFLAGS += $(CBMC_UNWINDSET) $(CBMC_CPROVER_LIBRARY_UNWINDSET) $(CBMC_DEFAULT_UNWIND) $(CBMC_OPT_CONFIG_LIBRARY) 823 | else ifeq ($(strip $(USE_DYNAMIC_FRAMES)),) 824 | CBMCFLAGS += $(CBMC_CPROVER_LIBRARY_UNWINDSET) $(CBMC_OPT_CONFIG_LIBRARY) 825 | endif 826 | endif 827 | 828 | $(LOGDIR)/result.xml: $(HARNESS_GOTO).goto 829 | $(LITANI) add-job \ 830 | $(POOL) \ 831 | --command \ 832 | '$(CBMC) $(CBMC_VERBOSITY) $(CBMCFLAGS) $(CBMC_FLAG_UNWINDING_ASSERTIONS) $(CHECKFLAGS) --trace --xml-ui $<' \ 833 | --inputs $^ \ 834 | --outputs $@ \ 835 | --ci-stage test \ 836 | --stdout-file $@ \ 837 | $(MEMORY_PROFILING) \ 838 | --ignore-returns 10 \ 839 | --timeout $(CBMC_TIMEOUT) \ 840 | --pipeline-name "$(PROOF_UID)" \ 841 | --tags "stats-group:safety checks" \ 842 | --stderr-file $(LOGDIR)/result-err-log.txt \ 843 | --description "$(PROOF_UID): checking safety properties" 844 | 845 | $(LOGDIR)/result.txt: $(HARNESS_GOTO).goto 846 | $(LITANI) add-job \ 847 | $(POOL) \ 848 | --command \ 849 | '$(CBMC) $(CBMC_VERBOSITY) $(CBMCFLAGS) $(CBMC_FLAG_UNWINDING_ASSERTIONS) $(CHECKFLAGS) --trace $<' \ 850 | --inputs $^ \ 851 | --outputs $@ \ 852 | --ci-stage test \ 853 | --stdout-file $@ \ 854 | $(MEMORY_PROFILING) \ 855 | --ignore-returns 10 \ 856 | --timeout $(CBMC_TIMEOUT) \ 857 | --pipeline-name "$(PROOF_UID)" \ 858 | --tags "stats-group:safety checks" \ 859 | --stderr-file $(LOGDIR)/result-err-log.txt \ 860 | --description "$(PROOF_UID): checking safety properties" 861 | 862 | $(LOGDIR)/property.xml: $(HARNESS_GOTO).goto 863 | $(LITANI) add-job \ 864 | --command \ 865 | '$(CBMC) $(CBMC_VERBOSITY) $(CBMCFLAGS) $(CBMC_FLAG_UNWINDING_ASSERTIONS) $(CHECKFLAGS) --show-properties --xml-ui $<' \ 866 | --inputs $^ \ 867 | --outputs $@ \ 868 | --ci-stage test \ 869 | --stdout-file $@ \ 870 | --ignore-returns 10 \ 871 | --pipeline-name "$(PROOF_UID)" \ 872 | --stderr-file $(LOGDIR)/property-err-log.txt \ 873 | --description "$(PROOF_UID): printing safety properties" 874 | 875 | $(LOGDIR)/coverage.xml: $(HARNESS_GOTO).goto 876 | $(LITANI) add-job \ 877 | $(POOL) \ 878 | --command \ 879 | '$(CBMC) $(CBMC_VERBOSITY) $(CBMCFLAGS) $(COVERFLAGS) --cover location --xml-ui $<' \ 880 | --inputs $^ \ 881 | --outputs $@ \ 882 | --ci-stage test \ 883 | --stdout-file $@ \ 884 | $(MEMORY_PROFILING) \ 885 | --ignore-returns 10 \ 886 | --timeout $(CBMC_TIMEOUT) \ 887 | --pipeline-name "$(PROOF_UID)" \ 888 | --tags "stats-group:coverage computation" \ 889 | --stderr-file $(LOGDIR)/coverage-err-log.txt \ 890 | --description "$(PROOF_UID): calculating coverage" 891 | 892 | COVERAGE ?= $(LOGDIR)/coverage.xml 893 | VIEWER_COVERAGE_FLAG ?= --coverage $(COVERAGE) 894 | 895 | $(PROOFDIR)/report: $(LOGDIR)/result.xml $(LOGDIR)/property.xml $(COVERAGE) 896 | $(LITANI) add-job \ 897 | --command " $(VIEWER) \ 898 | --result $(LOGDIR)/result.xml \ 899 | $(VIEWER_COVERAGE_FLAG) \ 900 | --property $(LOGDIR)/property.xml \ 901 | --srcdir $(SRCDIR) \ 902 | --goto $(HARNESS_GOTO).goto \ 903 | --reportdir $(PROOFDIR)/report \ 904 | --config $(PROOFDIR)/cbmc-viewer.json" \ 905 | --inputs $^ \ 906 | --outputs $(PROOFDIR)/report \ 907 | --pipeline-name "$(PROOF_UID)" \ 908 | --stdout-file $(LOGDIR)/viewer-log.txt \ 909 | --ci-stage report \ 910 | --description "$(PROOF_UID): generating report" 911 | 912 | litani-path: 913 | @echo $(LITANI) 914 | 915 | # ############################################################## 916 | # Phony Rules 917 | # 918 | # These rules provide a convenient way to run a single proof up to a 919 | # certain stage. Users can browse into a proof directory and run 920 | # "make -Bj 3 report" to generate a report for just that proof, or 921 | # "make goto" to build the goto binary. Under the hood, this runs litani 922 | # for just that proof. 923 | 924 | _goto: $(HARNESS_GOTO).goto 925 | goto: 926 | @ echo Running 'litani init' 927 | $(LITANI) init $(INIT_POOLS) --project $(PROJECT_NAME) 928 | @ echo Running 'litani add-job' 929 | $(MAKE) -B _goto 930 | @ echo Running 'litani build' 931 | $(LITANI) run-build 932 | 933 | _result: $(LOGDIR)/result.txt 934 | result: 935 | @ echo Running 'litani init' 936 | $(LITANI) init $(INIT_POOLS) --project $(PROJECT_NAME) 937 | @ echo Running 'litani add-job' 938 | $(MAKE) -B _result 939 | @ echo Running 'litani build' 940 | $(LITANI) run-build 941 | 942 | _property: $(LOGDIR)/property.xml 943 | property: 944 | @ echo Running 'litani init' 945 | $(LITANI) init $(INIT_POOLS) --project $(PROJECT_NAME) 946 | @ echo Running 'litani add-job' 947 | $(MAKE) -B _property 948 | @ echo Running 'litani build' 949 | $(LITANI) run-build 950 | 951 | _coverage: $(LOGDIR)/coverage.xml 952 | coverage: 953 | @ echo Running 'litani init' 954 | $(LITANI) init $(INIT_POOLS) --project $(PROJECT_NAME) 955 | @ echo Running 'litani add-job' 956 | $(MAKE) -B _coverage 957 | @ echo Running 'litani build' 958 | $(LITANI) run-build 959 | 960 | _report: $(PROOFDIR)/report 961 | report: 962 | @ echo Running 'litani init' 963 | $(LITANI) init $(INIT_POOLS) --project $(PROJECT_NAME) 964 | @ echo Running 'litani add-job' 965 | $(MAKE) -B _report 966 | @ echo Running 'litani build' 967 | $(LITANI) run-build 968 | 969 | _report_no_coverage: 970 | $(MAKE) COVERAGE="" VIEWER_COVERAGE_FLAG="" _report 971 | report-no-coverage: 972 | $(MAKE) COVERAGE="" VIEWER_COVERAGE_FLAG=" " report 973 | 974 | ################################################################ 975 | # Targets to clean up after ourselves 976 | clean: 977 | -$(RM) $(DEPENDENT_GOTOS) 978 | -$(RM) TAGS* 979 | -$(RM) *~ \#* 980 | -$(RM) $(REWRITTEN_SOURCES) $(foreach rs,$(REWRITTEN_SOURCES),$(rs).json) 981 | 982 | veryclean: clean 983 | -$(RM) -r report 984 | -$(RM) -r $(LOGDIR) $(GOTODIR) 985 | 986 | .PHONY: \ 987 | _coverage \ 988 | _goto \ 989 | _property \ 990 | _report \ 991 | _report_no_coverage \ 992 | clean \ 993 | coverage \ 994 | goto \ 995 | litani-path \ 996 | property \ 997 | report \ 998 | report-no-coverage \ 999 | result \ 1000 | setup_dependencies \ 1001 | testdeps \ 1002 | veryclean \ 1003 | # 1004 | 1005 | ################################################################ 1006 | 1007 | # Run "make echo-proof-uid" to print the proof ID of a proof. This can be 1008 | # used by scripts to ensure that every proof has an ID, that there are 1009 | # no duplicates, etc. 1010 | 1011 | .PHONY: echo-proof-uid 1012 | echo-proof-uid: 1013 | @echo $(PROOF_UID) 1014 | 1015 | .PHONY: echo-project-name 1016 | echo-project-name: 1017 | @echo $(PROJECT_NAME) 1018 | 1019 | ################################################################ 1020 | 1021 | # Project-specific targets requiring values defined above 1022 | sinclude $(PROOF_ROOT)/Makefile-project-targets 1023 | 1024 | # CI-specific targets to drive cbmc in CI 1025 | sinclude $(PROOF_ROOT)/Makefile-project-testing 1026 | 1027 | ################################################################ 1028 | -------------------------------------------------------------------------------- /test/cbmc/proofs/README.md: -------------------------------------------------------------------------------- 1 | CBMC proofs 2 | =========== 3 | 4 | This directory contains the CBMC proofs. Each proof is in its own 5 | directory. 6 | 7 | This directory includes four Makefiles. 8 | 9 | One Makefile describes the basic workflow for building and running proofs: 10 | 11 | * Makefile.common: 12 | * make: builds the goto binary, does the cbmc property checking 13 | and coverage checking, and builds the final report. 14 | * make goto: builds the goto binary 15 | * make result: does cbmc property checking 16 | * make coverage: does cbmc coverage checking 17 | * make report: builds the final report 18 | 19 | Three included Makefiles describe project-specific settings and can override 20 | definitions in Makefile.common: 21 | 22 | * Makefile-project-defines: definitions like compiler flags 23 | required to build the goto binaries, and definitions to override 24 | definitions in Makefile.common. 25 | * Makefile-project-targets: other make targets needed for the project 26 | * Makefile-project-testing: other definitions and targets needed for 27 | unit testing or continuous integration. 28 | -------------------------------------------------------------------------------- /test/cbmc/proofs/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/629ec2a21d91ade13bfb995aace2223b36c4cd2e/test/cbmc/proofs/lib/__init__.py -------------------------------------------------------------------------------- /test/cbmc/proofs/lib/print_tool_versions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: MIT-0 5 | 6 | 7 | import logging 8 | import pathlib 9 | import shutil 10 | import subprocess 11 | 12 | 13 | _TOOLS = [ 14 | "cadical", 15 | "cbmc", 16 | "cbmc-viewer", 17 | "cbmc-starter-kit-update", 18 | "kissat", 19 | "litani", 20 | ] 21 | 22 | 23 | def _format_versions(table): 24 | lines = [ 25 | "", 26 | '', 27 | ] 28 | for tool, version in table.items(): 29 | if version: 30 | v_str = f'
{version}
' 31 | else: 32 | v_str = 'not found' 33 | lines.append( 34 | f'' 36 | f'') 37 | lines.append("
Tool Versions
{tool}:{v_str}
") 38 | return "\n".join(lines) 39 | 40 | 41 | def _get_tool_versions(): 42 | ret = {} 43 | for tool in _TOOLS: 44 | err = f"Could not determine version of {tool}: " 45 | ret[tool] = None 46 | if not shutil.which(tool): 47 | logging.error("%s'%s' not found on $PATH", err, tool) 48 | continue 49 | cmd = [tool, "--version"] 50 | proc = subprocess.Popen(cmd, text=True, stdout=subprocess.PIPE) 51 | try: 52 | out, _ = proc.communicate(timeout=10) 53 | except subprocess.TimeoutExpired: 54 | logging.error("%s'%s --version' timed out", err, tool) 55 | continue 56 | if proc.returncode: 57 | logging.error( 58 | "%s'%s --version' returned %s", err, tool, str(proc.returncode)) 59 | continue 60 | ret[tool] = out.strip() 61 | return ret 62 | 63 | 64 | def main(): 65 | exe_name = pathlib.Path(__file__).name 66 | logging.basicConfig(format=f"{exe_name}: %(message)s") 67 | 68 | table = _get_tool_versions() 69 | out = _format_versions(table) 70 | print(out) 71 | 72 | 73 | if __name__ == "__main__": 74 | main() 75 | -------------------------------------------------------------------------------- /test/cbmc/proofs/lib/summarize.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | import argparse 5 | import json 6 | import logging 7 | import os 8 | import sys 9 | 10 | 11 | DESCRIPTION = """Print 2 tables in GitHub-flavored Markdown that summarize 12 | an execution of CBMC proofs.""" 13 | 14 | 15 | def get_args(): 16 | """Parse arguments for summarize script.""" 17 | parser = argparse.ArgumentParser(description=DESCRIPTION) 18 | for arg in [{ 19 | "flags": ["--run-file"], 20 | "help": "path to the Litani run.json file", 21 | "required": True, 22 | }]: 23 | flags = arg.pop("flags") 24 | parser.add_argument(*flags, **arg) 25 | return parser.parse_args() 26 | 27 | 28 | def _get_max_length_per_column_list(data): 29 | ret = [len(item) + 1 for item in data[0]] 30 | for row in data[1:]: 31 | for idx, item in enumerate(row): 32 | ret[idx] = max(ret[idx], len(item) + 1) 33 | return ret 34 | 35 | 36 | def _get_table_header_separator(max_length_per_column_list): 37 | line_sep = "" 38 | for max_length_of_word_in_col in max_length_per_column_list: 39 | line_sep += "|" + "-" * (max_length_of_word_in_col + 1) 40 | line_sep += "|\n" 41 | return line_sep 42 | 43 | 44 | def _get_entries(max_length_per_column_list, row_data): 45 | entries = [] 46 | for row in row_data: 47 | entry = "" 48 | for idx, word in enumerate(row): 49 | max_length_of_word_in_col = max_length_per_column_list[idx] 50 | space_formatted_word = (max_length_of_word_in_col - len(word)) * " " 51 | entry += "| " + word + space_formatted_word 52 | entry += "|\n" 53 | entries.append(entry) 54 | return entries 55 | 56 | 57 | def _get_rendered_table(data): 58 | table = [] 59 | max_length_per_column_list = _get_max_length_per_column_list(data) 60 | entries = _get_entries(max_length_per_column_list, data) 61 | for idx, entry in enumerate(entries): 62 | if idx == 1: 63 | line_sep = _get_table_header_separator(max_length_per_column_list) 64 | table.append(line_sep) 65 | table.append(entry) 66 | table.append("\n") 67 | return "".join(table) 68 | 69 | 70 | def _get_status_and_proof_summaries(run_dict): 71 | """Parse a dict representing a Litani run and create lists summarizing the 72 | proof results. 73 | 74 | Parameters 75 | ---------- 76 | run_dict 77 | A dictionary representing a Litani run. 78 | 79 | 80 | Returns 81 | ------- 82 | A list of 2 lists. 83 | The first sub-list maps a status to the number of proofs with that status. 84 | The second sub-list maps each proof to its status. 85 | """ 86 | count_statuses = {} 87 | proofs = [["Proof", "Status"]] 88 | for proof_pipeline in run_dict["pipelines"]: 89 | status_pretty_name = proof_pipeline["status"].title().replace("_", " ") 90 | try: 91 | count_statuses[status_pretty_name] += 1 92 | except KeyError: 93 | count_statuses[status_pretty_name] = 1 94 | if proof_pipeline["name"] == "print_tool_versions": 95 | continue 96 | proofs.append([proof_pipeline["name"], status_pretty_name]) 97 | statuses = [["Status", "Count"]] 98 | for status, count in count_statuses.items(): 99 | statuses.append([status, str(count)]) 100 | return [statuses, proofs] 101 | 102 | 103 | def print_proof_results(out_file): 104 | """ 105 | Print 2 strings that summarize the proof results. 106 | When printing, each string will render as a GitHub flavored Markdown table. 107 | """ 108 | output = "## Summary of CBMC proof results\n\n" 109 | with open(out_file, encoding='utf-8') as run_json: 110 | run_dict = json.load(run_json) 111 | status_table, proof_table = _get_status_and_proof_summaries(run_dict) 112 | for summary in (status_table, proof_table): 113 | output += _get_rendered_table(summary) 114 | 115 | print(output) 116 | sys.stdout.flush() 117 | 118 | github_summary_file = os.getenv("GITHUB_STEP_SUMMARY") 119 | if github_summary_file: 120 | with open(github_summary_file, "a") as handle: 121 | print(output, file=handle) 122 | handle.flush() 123 | else: 124 | logging.warning( 125 | "$GITHUB_STEP_SUMMARY not set, not writing summary file") 126 | 127 | msg = ( 128 | "Click the 'Summary' button to view a Markdown table " 129 | "summarizing all proof results") 130 | if run_dict["status"] != "success": 131 | logging.error("Not all proofs passed.") 132 | logging.error(msg) 133 | sys.exit(1) 134 | logging.info(msg) 135 | 136 | 137 | if __name__ == '__main__': 138 | args = get_args() 139 | logging.basicConfig(format="%(levelname)s: %(message)s") 140 | try: 141 | print_proof_results(args.run_file) 142 | except Exception as ex: # pylint: disable=broad-except 143 | logging.critical("Could not print results. Exception: %s", str(ex)) -------------------------------------------------------------------------------- /test/cbmc/proofs/prepare.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | 7 | """Prepare the source tree for proofs in continuous integration.""" 8 | 9 | 10 | import os 11 | import subprocess 12 | 13 | 14 | MAKEFILE = "Makefile" 15 | CBMC_BATCH_YAML = "cbmc-batch.yaml" 16 | 17 | 18 | def create_cbmc_batch_yaml(folder): 19 | """Run make to create cbmc-batch.yaml in folder.""" 20 | 21 | try: 22 | subprocess.run( 23 | ["make", "-B", CBMC_BATCH_YAML], 24 | cwd=folder, 25 | stdout=subprocess.PIPE, 26 | stderr=subprocess.PIPE, 27 | universal_newlines=True, 28 | check=True 29 | ) 30 | except subprocess.CalledProcessError as error: 31 | raise UserWarning("Failed to create {} in {}: " 32 | "command was '{}': " 33 | "error was '{}'" 34 | .format(CBMC_BATCH_YAML, folder, 35 | ' '.join(error.cmd), 36 | error.stderr.strip())) from None 37 | 38 | 39 | def create_cbmc_batch_yaml_files(root='.'): 40 | """Create cbmc-batch.yaml in all directories under root.""" 41 | 42 | for folder, _, files in os.walk(root): 43 | if CBMC_BATCH_YAML in files and MAKEFILE in files: 44 | create_cbmc_batch_yaml(folder) 45 | 46 | 47 | def prepare(): 48 | """Prepare the source tree for proofs in continuous integration.""" 49 | 50 | create_cbmc_batch_yaml_files() 51 | 52 | 53 | if __name__ == "__main__": 54 | prepare() 55 | -------------------------------------------------------------------------------- /test/cbmc/proofs/run-cbmc-proofs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: MIT-0 5 | 6 | 7 | import argparse 8 | import asyncio 9 | import json 10 | import logging 11 | import math 12 | import os 13 | import pathlib 14 | import re 15 | import subprocess 16 | import sys 17 | import tempfile 18 | 19 | from lib.summarize import print_proof_results 20 | 21 | 22 | DESCRIPTION = "Configure and run all CBMC proofs in parallel" 23 | 24 | # Keep the epilog hard-wrapped at 70 characters, as it gets printed 25 | # verbatim in the terminal. 70 characters stops here --------------> | 26 | EPILOG = """ 27 | This tool automates the process of running `make report` in each of 28 | the CBMC proof directories. The tool calculates the dependency graph 29 | of all tasks needed to build, run, and report on all the proofs, and 30 | executes these tasks in parallel. 31 | 32 | The tool is roughly equivalent to doing this: 33 | 34 | litani init --project "my-cool-project"; 35 | 36 | find . -name cbmc-proof.txt | while read -r proof; do 37 | pushd $(dirname ${proof}); 38 | 39 | # The `make _report` rule adds a single proof to litani 40 | # without running it 41 | make _report; 42 | 43 | popd; 44 | done 45 | 46 | litani run-build; 47 | 48 | except that it is much faster and provides some convenience options. 49 | The CBMC CI runs this script with no arguments to build and run all 50 | proofs in parallel. The value of "my-cool-project" is taken from the 51 | PROJECT_NAME variable in Makefile-project-defines. 52 | 53 | The --no-standalone argument omits the `litani init` and `litani 54 | run-build`; use it when you want to add additional proof jobs, not 55 | just the CBMC ones. In that case, you would run `litani init` 56 | yourself; then run `run-cbmc-proofs --no-standalone`; add any 57 | additional jobs that you want to execute with `litani add-job`; and 58 | finally run `litani run-build`. 59 | 60 | The litani dashboard will be written under the `output` directory; the 61 | cbmc-viewer reports remain in the `$PROOF_DIR/report` directory. The 62 | HTML dashboard from the latest Litani run will always be symlinked to 63 | `output/latest/html/index.html`, so you can keep that page open in 64 | your browser and reload the page whenever you re-run this script. 65 | """ 66 | # 70 characters stops here ----------------------------------------> | 67 | 68 | 69 | def get_project_name(): 70 | cmd = [ 71 | "make", 72 | "--no-print-directory", 73 | "-f", "Makefile.common", 74 | "echo-project-name", 75 | ] 76 | logging.debug(" ".join(cmd)) 77 | proc = subprocess.run(cmd, universal_newlines=True, stdout=subprocess.PIPE, check=False) 78 | if proc.returncode: 79 | logging.critical("could not run make to determine project name") 80 | sys.exit(1) 81 | if not proc.stdout.strip(): 82 | logging.warning( 83 | "project name has not been set; using generic name instead. " 84 | "Set the PROJECT_NAME value in Makefile-project-defines to " 85 | "remove this warning") 86 | return "" 87 | return proc.stdout.strip() 88 | 89 | 90 | def get_args(): 91 | pars = argparse.ArgumentParser( 92 | description=DESCRIPTION, epilog=EPILOG, 93 | formatter_class=argparse.RawDescriptionHelpFormatter) 94 | for arg in [{ 95 | "flags": ["-j", "--parallel-jobs"], 96 | "type": int, 97 | "metavar": "N", 98 | "help": "run at most N proof jobs in parallel", 99 | }, { 100 | "flags": ["--fail-on-proof-failure"], 101 | "action": "store_true", 102 | "help": "exit with return code `10' if any proof failed" 103 | " (default: exit 0)", 104 | }, { 105 | "flags": ["--no-standalone"], 106 | "action": "store_true", 107 | "help": "only configure proofs: do not initialize nor run", 108 | }, { 109 | "flags": ["-p", "--proofs"], 110 | "nargs": "+", 111 | "metavar": "DIR", 112 | "help": "only run proof in directory DIR (can pass more than one)", 113 | }, { 114 | "flags": ["--project-name"], 115 | "metavar": "NAME", 116 | "default": get_project_name(), 117 | "help": "project name for report. Default: %(default)s", 118 | }, { 119 | "flags": ["--marker-file"], 120 | "metavar": "FILE", 121 | "default": "cbmc-proof.txt", 122 | "help": ( 123 | "name of file that marks proof directories. Default: " 124 | "%(default)s"), 125 | }, { 126 | "flags": ["--no-memory-profile"], 127 | "action": "store_true", 128 | "help": "disable memory profiling, even if Litani supports it" 129 | }, { 130 | "flags": ["--no-expensive-limit"], 131 | "action": "store_true", 132 | "help": "do not limit parallelism of 'EXPENSIVE' jobs", 133 | }, { 134 | "flags": ["--expensive-jobs-parallelism"], 135 | "metavar": "N", 136 | "default": 1, 137 | "type": int, 138 | "help": ( 139 | "how many proof jobs marked 'EXPENSIVE' to run in parallel. " 140 | "Default: %(default)s"), 141 | }, { 142 | "flags": ["--verbose"], 143 | "action": "store_true", 144 | "help": "verbose output", 145 | }, { 146 | "flags": ["--debug"], 147 | "action": "store_true", 148 | "help": "debug output", 149 | }, { 150 | "flags": ["--summarize"], 151 | "action": "store_true", 152 | "help": "summarize proof results with two tables on stdout", 153 | }, { 154 | "flags": ["--version"], 155 | "action": "version", 156 | "version": "CBMC starter kit 2.5", 157 | "help": "display version and exit" 158 | }]: 159 | flags = arg.pop("flags") 160 | pars.add_argument(*flags, **arg) 161 | return pars.parse_args() 162 | 163 | 164 | def set_up_logging(verbose): 165 | if verbose: 166 | level = logging.DEBUG 167 | else: 168 | level = logging.WARNING 169 | logging.basicConfig( 170 | format="run-cbmc-proofs: %(message)s", level=level) 171 | 172 | 173 | def task_pool_size(): 174 | ret = os.cpu_count() 175 | if ret is None or ret < 3: 176 | return 1 177 | return ret - 2 178 | 179 | 180 | def print_counter(counter): 181 | # pylint: disable=consider-using-f-string 182 | print("\rConfiguring CBMC proofs: " 183 | "{complete:{width}} / {total:{width}}".format(**counter), end="", file=sys.stderr) 184 | 185 | 186 | def get_proof_dirs(proof_root, proof_list, marker_file): 187 | if proof_list is not None: 188 | proofs_remaining = list(proof_list) 189 | else: 190 | proofs_remaining = [] 191 | 192 | for root, _, fyles in os.walk(proof_root): 193 | proof_name = str(pathlib.Path(root).name) 194 | if root != str(proof_root) and ".litani_cache_dir" in fyles: 195 | pathlib.Path(f"{root}/.litani_cache_dir").unlink() 196 | if proof_list and proof_name not in proof_list: 197 | continue 198 | if proof_list and proof_name in proofs_remaining: 199 | proofs_remaining.remove(proof_name) 200 | if marker_file in fyles: 201 | yield root 202 | 203 | if proofs_remaining: 204 | logging.critical( 205 | "The following proofs were not found: %s", 206 | ", ".join(proofs_remaining)) 207 | sys.exit(1) 208 | 209 | 210 | def run_build(litani, jobs, fail_on_proof_failure, summarize): 211 | cmd = [str(litani), "run-build"] 212 | if jobs: 213 | cmd.extend(["-j", str(jobs)]) 214 | if fail_on_proof_failure: 215 | cmd.append("--fail-on-pipeline-failure") 216 | if summarize: 217 | out_file = pathlib.Path(tempfile.gettempdir(), "run.json").resolve() 218 | cmd.extend(["--out-file", str(out_file)]) 219 | 220 | logging.debug(" ".join(cmd)) 221 | proc = subprocess.run(cmd, check=False) 222 | 223 | if proc.returncode and not fail_on_proof_failure: 224 | logging.critical("Failed to run litani run-build") 225 | sys.exit(1) 226 | 227 | if summarize: 228 | print_proof_results(out_file) 229 | out_file.unlink() 230 | 231 | if proc.returncode: 232 | logging.error("One or more proofs failed") 233 | sys.exit(10) 234 | 235 | def get_litani_path(proof_root): 236 | cmd = [ 237 | "make", 238 | "--no-print-directory", 239 | f"PROOF_ROOT={proof_root}", 240 | "-f", "Makefile.common", 241 | "litani-path", 242 | ] 243 | logging.debug(" ".join(cmd)) 244 | proc = subprocess.run(cmd, universal_newlines=True, stdout=subprocess.PIPE, check=False) 245 | if proc.returncode: 246 | logging.critical("Could not determine path to litani") 247 | sys.exit(1) 248 | return proc.stdout.strip() 249 | 250 | 251 | def get_litani_capabilities(litani_path): 252 | cmd = [litani_path, "print-capabilities"] 253 | proc = subprocess.run( 254 | cmd, text=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=False) 255 | if proc.returncode: 256 | return [] 257 | try: 258 | return json.loads(proc.stdout) 259 | except RuntimeError: 260 | logging.warning("Could not load litani capabilities: '%s'", proc.stdout) 261 | return [] 262 | 263 | 264 | def check_uid_uniqueness(proof_dir, proof_uids): 265 | with (pathlib.Path(proof_dir) / "Makefile").open() as handle: 266 | for line in handle: 267 | match = re.match(r"^PROOF_UID\s*=\s*(?P\w+)", line) 268 | if not match: 269 | continue 270 | if match["uid"] not in proof_uids: 271 | proof_uids[match["uid"]] = proof_dir 272 | return 273 | 274 | logging.critical( 275 | "The Makefile in directory '%s' should have a different " 276 | "PROOF_UID than the Makefile in directory '%s'", 277 | proof_dir, proof_uids[match["uid"]]) 278 | sys.exit(1) 279 | 280 | logging.critical( 281 | "The Makefile in directory '%s' should contain a line like", proof_dir) 282 | logging.critical("PROOF_UID = ...") 283 | logging.critical("with a unique identifier for the proof.") 284 | sys.exit(1) 285 | 286 | 287 | def should_enable_memory_profiling(litani_caps, args): 288 | if args.no_memory_profile: 289 | return False 290 | return "memory_profile" in litani_caps 291 | 292 | 293 | def should_enable_pools(litani_caps, args): 294 | if args.no_expensive_limit: 295 | return False 296 | return "pools" in litani_caps 297 | 298 | 299 | async def configure_proof_dirs( # pylint: disable=too-many-arguments 300 | queue, counter, proof_uids, enable_pools, enable_memory_profiling, debug): 301 | while True: 302 | print_counter(counter) 303 | path = str(await queue.get()) 304 | 305 | check_uid_uniqueness(path, proof_uids) 306 | 307 | pools = ["ENABLE_POOLS=true"] if enable_pools else [] 308 | profiling = [ 309 | "ENABLE_MEMORY_PROFILING=true"] if enable_memory_profiling else [] 310 | 311 | # Allow interactive tasks to preempt proof configuration 312 | proc = await asyncio.create_subprocess_exec( 313 | "nice", "-n", "15", "make", *pools, 314 | *profiling, "-B", "_report", "" if debug else "--quiet", cwd=path, 315 | stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) 316 | stdout, stderr = await proc.communicate() 317 | logging.debug("returncode: %s", str(proc.returncode)) 318 | logging.debug("stdout:") 319 | for line in stdout.decode().splitlines(): 320 | logging.debug(line) 321 | logging.debug("stderr:") 322 | for line in stderr.decode().splitlines(): 323 | logging.debug(line) 324 | 325 | counter["fail" if proc.returncode else "pass"].append(path) 326 | counter["complete"] += 1 327 | 328 | print_counter(counter) 329 | queue.task_done() 330 | 331 | 332 | async def main(): # pylint: disable=too-many-locals 333 | args = get_args() 334 | set_up_logging(args.verbose) 335 | 336 | proof_root = pathlib.Path(os.getcwd()) 337 | litani = get_litani_path(proof_root) 338 | 339 | litani_caps = get_litani_capabilities(litani) 340 | enable_pools = should_enable_pools(litani_caps, args) 341 | init_pools = [ 342 | "--pools", f"expensive:{args.expensive_jobs_parallelism}" 343 | ] if enable_pools else [] 344 | 345 | if not args.no_standalone: 346 | cmd = [ 347 | str(litani), "init", *init_pools, "--project", args.project_name, 348 | "--no-print-out-dir", 349 | ] 350 | 351 | if "output_directory_flags" in litani_caps: 352 | out_prefix = proof_root / "output" 353 | out_symlink = out_prefix / "latest" 354 | out_index = out_symlink / "html" / "index.html" 355 | cmd.extend([ 356 | "--output-prefix", str(out_prefix), 357 | "--output-symlink", str(out_symlink), 358 | ]) 359 | print( 360 | "\nFor your convenience, the output of this run will be symbolically linked to ", 361 | out_index, "\n") 362 | 363 | logging.debug(" ".join(cmd)) 364 | proc = subprocess.run(cmd, check=False) 365 | if proc.returncode: 366 | logging.critical("Failed to run litani init") 367 | sys.exit(1) 368 | 369 | proof_dirs = list(get_proof_dirs( 370 | proof_root, args.proofs, args.marker_file)) 371 | if not proof_dirs: 372 | logging.critical("No proof directories found") 373 | sys.exit(1) 374 | 375 | proof_queue = asyncio.Queue() 376 | for proof_dir in proof_dirs: 377 | proof_queue.put_nowait(proof_dir) 378 | 379 | counter = { 380 | "pass": [], 381 | "fail": [], 382 | "complete": 0, 383 | "total": len(proof_dirs), 384 | "width": int(math.log10(len(proof_dirs))) + 1 385 | } 386 | 387 | proof_uids = {} 388 | tasks = [] 389 | 390 | enable_memory_profiling = should_enable_memory_profiling(litani_caps, args) 391 | 392 | for _ in range(task_pool_size()): 393 | task = asyncio.create_task(configure_proof_dirs( 394 | proof_queue, counter, proof_uids, enable_pools, 395 | enable_memory_profiling, args.debug)) 396 | tasks.append(task) 397 | 398 | await proof_queue.join() 399 | 400 | print_counter(counter) 401 | print("", file=sys.stderr) 402 | 403 | if counter["fail"]: 404 | logging.critical( 405 | "Failed to configure the following proofs:\n%s", "\n".join( 406 | [str(f) for f in counter["fail"]])) 407 | sys.exit(1) 408 | 409 | if not args.no_standalone: 410 | run_build(litani, args.parallel_jobs, args.fail_on_proof_failure, args.summarize) 411 | 412 | 413 | if __name__ == "__main__": 414 | asyncio.run(main()) 415 | -------------------------------------------------------------------------------- /test/cbmc/sources/README.md: -------------------------------------------------------------------------------- 1 | CBMC proof source code 2 | ====================== 3 | 4 | This directory contains source code written for CBMC proofs. It is 5 | common to write some code to model aspects of the system under test, 6 | and this code goes here. 7 | -------------------------------------------------------------------------------- /test/cbmc/stubs/README.md: -------------------------------------------------------------------------------- 1 | CBMC proof stubs 2 | ====================== 3 | 4 | This directory contains the stubs written for CBMC proofs. It is 5 | common to stub out functionality like network send and receive methods 6 | when writing a CBMC proof, and the code for these stubs goes here. 7 | -------------------------------------------------------------------------------- /test/include/fleet_provisioning_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AWS IoT Fleet Provisioning v1.2.1 3 | * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | * 5 | * SPDX-License-Identifier: MIT 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | * this software and associated documentation files (the "Software"), to deal in 9 | * the Software without restriction, including without limitation the rights to 10 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | * the Software, and to permit persons to whom the Software is furnished to do so, 12 | * subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @file fleet_provisioning_config.h 27 | * @brief Config values for testing the AWS IoT Fleet Provisioning Library. 28 | */ 29 | 30 | #ifndef FLEET_PROVISIONING_CONFIG_H_ 31 | #define FLEET_PROVISIONING_CONFIG_H_ 32 | 33 | #include 34 | 35 | #ifdef DISABLE_LOGGING 36 | #ifndef LogError 37 | #define LogError( message ) 38 | #endif 39 | #ifndef LogWarn 40 | #define LogWarn( message ) 41 | #endif 42 | 43 | #ifndef LogInfo 44 | #define LogInfo( message ) 45 | #endif 46 | 47 | #ifndef LogDebug 48 | #define LogDebug( message ) 49 | #endif 50 | 51 | #else /* ! DISABLE_LOGGING */ 52 | #define LogError( message ) printf( "Error: " ); printf message; printf( "\n" ) 53 | 54 | #define LogWarn( message ) printf( "Warn: " ); printf message; printf( "\n" ) 55 | 56 | #define LogInfo( message ) printf( "Info: " ); printf message; printf( "\n" ) 57 | 58 | #define LogDebug( message ) printf( "Debug: " ); printf message; printf( "\n" ) 59 | #endif /* DISABLE_LOGGING */ 60 | 61 | #endif /* FLEET_PROVISIONING_CONFIG_H_ */ 62 | -------------------------------------------------------------------------------- /test/unit-test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Include filepaths for Fleet Provisioning library. 2 | include( ${MODULE_ROOT_DIR}/fleetprovisioningFilePaths.cmake ) 3 | 4 | set( library_name "fleet_provisioning" ) 5 | set( library_target_name "${library_name}_target" ) 6 | set( utest_binary_name "${library_name}_utest" ) 7 | 8 | # =========================== Library ============================== 9 | 10 | # List of library source files. 11 | list( APPEND library_source_files 12 | ${FLEET_PROVISIONING_SOURCES} ) 13 | 14 | # List of library include directories. 15 | list( APPEND library_include_directories 16 | ${FLEET_PROVISIONING_INCLUDE_PUBLIC_DIRS} 17 | "${CMAKE_CURRENT_LIST_DIR}/../include" ) 18 | 19 | # Create a target for building library. 20 | create_library_target( ${library_target_name} 21 | "${library_source_files}" 22 | "${library_include_directories}" ) 23 | 24 | # =========================== Test Binary ============================== 25 | 26 | # The source file containing the unit tests. 27 | set( utest_source_file "fleet_provisioning_utest.c" ) 28 | 29 | # The list of include directories for the test binary target. 30 | list( APPEND utest_include_directories 31 | ${FLEET_PROVISIONING_INCLUDE_PUBLIC_DIRS} ) 32 | 33 | # Libraries to be linked while building the test binary. 34 | list( APPEND utest_link_list 35 | lib${library_target_name}.a ) 36 | 37 | # The targets on which the test binary target depends. 38 | list( APPEND utest_dep_list 39 | ${library_target_name} ) 40 | 41 | # Create a target for the test binary. 42 | create_test_binary_target( ${utest_binary_name} 43 | ${utest_source_file} 44 | "${utest_link_list}" 45 | "${utest_dep_list}" 46 | "${test_include_directories}" ) 47 | -------------------------------------------------------------------------------- /test/unit-test/unity_build.cmake: -------------------------------------------------------------------------------- 1 | # Macro to clone the Unity submodule. 2 | macro( clone_unity ) 3 | find_package( Git REQUIRED ) 4 | message( "Cloning Unity submodule." ) 5 | execute_process( COMMAND rm -rf ${UNITY_DIR} 6 | COMMAND ${GIT_EXECUTABLE} submodule update --checkout --init --recursive ${UNITY_DIR} 7 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 8 | RESULT_VARIABLE UNITY_CLONE_RESULT ) 9 | 10 | if( NOT ${UNITY_CLONE_RESULT} STREQUAL "0" ) 11 | message( FATAL_ERROR "Failed to clone Unity submodule." ) 12 | endif() 13 | endmacro() 14 | 15 | # Macro to add library target for Unity to build configuration. 16 | macro( add_unity_target ) 17 | add_library( unity STATIC 18 | "${UNITY_DIR}/src/unity.c" 19 | "${UNITY_DIR}/extras/fixture/src/unity_fixture.c" 20 | "${UNITY_DIR}/extras/memory/src/unity_memory.c" ) 21 | 22 | target_include_directories( unity PUBLIC 23 | "${UNITY_DIR}/src/" 24 | "${UNITY_DIR}/extras/fixture/src" 25 | "${UNITY_DIR}/extras/memory/src" ) 26 | 27 | set_target_properties( unity PROPERTIES 28 | ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib 29 | POSITION_INDEPENDENT_CODE ON ) 30 | 31 | target_link_libraries( unity ) 32 | endmacro() 33 | -------------------------------------------------------------------------------- /tools/coverity/README.md: -------------------------------------------------------------------------------- 1 | # Static code analysis for AWS IoT Fleet Provisioning Library 2 | This directory is made for the purpose of statically testing the MISRA C:2012 compliance of AWS IoT Fleet Provisioning Library using 3 | [Synopsys Coverity](https://www.synopsys.com/software-integrity/security-testing/static-analysis-sast.html) static analysis tool. 4 | To that end, this directory provides a [configuration file](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/blob/main/tools/coverity/misra.config) to use when 5 | building a binary for the tool to analyze. 6 | 7 | > **Note** 8 | For generating the report as outlined below, we have used Coverity version 2023.6.1. 9 | 10 | For details regarding the suppressed violations in the report (which can be generated using the instructions described below), please 11 | see the [MISRA.md](https://github.com/aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk/blob/main/MISRA.md) file. 12 | 13 | ## Getting Started 14 | ### Prerequisites 15 | You can run this on a platform supported by Coverity. The list and other details can be found [here](https://documentation.blackduck.com/bundle/coverity-docs-2024.9/page/deploy-install-guide/topics/supported_platforms_for_coverity_analysis.html). 16 | To compile and run the Coverity target successfully, you must have the following: 17 | 18 | 1. CMake version > 3.13.0 (You can check whether you have this by typing `cmake --version`) 19 | 2. GCC compiler 20 | - You can see the downloading and installation instructions [here](https://gcc.gnu.org/install/). 21 | 3. Download the repo and include the submodules using the following commands. 22 | - `git clone --recurse-submodules git@github.com:aws/Fleet-Provisioning-for-AWS-IoT-embedded-sdk.git ./Fleet-Provisioning-for-AWS-IoT-embedded-sdk` 23 | - `cd ./Fleet-Provisioning-for-AWS-IoT-embedded-sdk` 24 | - `git submodule update --checkout --init --recursive` 25 | 26 | ### To build and run coverity: 27 | Go to the root directory of the library and run the following commands in terminal: 28 | 1. Update the compiler configuration in Coverity 29 | ~~~ 30 | cov-configure --force --compiler cc --comptype gcc 31 | ~~~ 32 | 2. Create the build files using CMake in a `build` directory 33 | ~~~ 34 | cmake -B build -S test -DCOV_ANALYSIS=1 35 | ~~~ 36 | 3. Go to the build directory and copy the coverity configuration file 37 | ~~~ 38 | cd build/ 39 | ~~~ 40 | 4. Build the static analysis target 41 | ~~~ 42 | cov-build --emit-complementary-info --dir cov-out make coverity_analysis 43 | ~~~ 44 | 5. Go to the Coverity output directory (`cov-out`) and begin Coverity static analysis 45 | ~~~ 46 | cd cov-out/ 47 | cov-analyze --dir . --coding-standard-config ../../tools/coverity/misra.config --tu-pattern "file('.*/source/.*')" 48 | ~~~ 49 | 6. Format the errors in HTML format so that it is more readable while removing the test and build directory from the report 50 | ~~~ 51 | cov-format-errors --dir . --file "source" --exclude-files '(/build/|/test/)' --html-output html-out; 52 | ~~~ 53 | 7. Format the errors in JSON format to perform a jq query to get a simplified list of any exceptions. 54 | NOTE: A blank output means there are no defects that aren't being suppressed by the config or inline comments. 55 | ~~~ 56 | cov-format-errors --dir . --file "source" --exclude-files '(/build/|/test/)' --json-output-v2 defects.json; 57 | echo -e "\n-------------------------Non-Suppresed Deviations, if any, Listed Below-------------------------\n"; 58 | jq '.issues[] | .events[] | .eventTag ' defects.json | sort | uniq -c | sort -nr; 59 | echo -e "\n-------------------------Non-Suppresed Deviations, if any, Listed Above-------------------------\n"; 60 | ~~~ 61 | 62 | For your convenience the commands above are below to be copy/pasted into a UNIX command friendly terminal. 63 | ~~~ 64 | cov-configure --force --compiler cc --comptype gcc; 65 | cmake -B build -S test -DCOV_ANALYSIS=1; 66 | cd build/; 67 | cov-build --emit-complementary-info --dir cov-out make coverity_analysis; 68 | cd cov-out/ 69 | cov-analyze --dir . --coding-standard-config ../../tools/coverity/misra.config --tu-pattern "file('.*/source/.*')"; 70 | cov-format-errors --dir . --file "source" --exclude-files '(/build/|/test/)' --html-output html-out; 71 | cov-format-errors --dir . --file "source" --exclude-files '(/build/|/test/)' --json-output-v2 defects.json; 72 | echo -e "\n-------------------------Non-Suppresed Deviations, if any, Listed Below-------------------------\n"; 73 | jq '.issues[] | .events[] | .eventTag ' defects.json | sort | uniq -c | sort -nr; 74 | echo -e "\n-------------------------Non-Suppresed Deviations, if any, Listed Above-------------------------\n"; 75 | cd ../../; 76 | ~~~ 77 | 78 | You should now have the HTML formatted violations list in a directory named `build/cov-out/html-output`. 79 | With the current configuration and the provided project, you should not see any deviations. 80 | -------------------------------------------------------------------------------- /tools/coverity/misra.config: -------------------------------------------------------------------------------- 1 | { 2 | "version" : "2.0", 3 | "standard" : "c2012", 4 | "title": "Coverity MISRA Configuration", 5 | "deviations" : [ 6 | { 7 | "deviation": "Directive 4.9", 8 | "reason": "Allow inclusion of function like macros. Asserts, logging, and topic string macros use function like macros." 9 | }, 10 | { 11 | "deviation": "Rule 2.5", 12 | "reason": "Allow unused macros. Macros defined for topic strings are not used by the library, but are part of the API." 13 | }, 14 | { 15 | "deviation": "Rule 3.1", 16 | "reason": "Allow nested comments. C++ style `//` comments are used in example code within Doxygen documentation blocks." 17 | }, 18 | { 19 | "deviation": "Rule 8.7", 20 | "reason": "API functions are not used by library. They must be externally visible in order to be used by the application." 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /tools/unity/coverage.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required( VERSION 3.13 ) 2 | set( BINARY_DIR ${CMAKE_BINARY_DIR} ) 3 | 4 | # Reset coverage counters. 5 | execute_process( COMMAND lcov --directory ${CMAKE_BINARY_DIR} 6 | --base-directory ${CMAKE_BINARY_DIR} 7 | --zerocounters ) 8 | 9 | # Create directory for results. 10 | execute_process( COMMAND mkdir -p ${CMAKE_BINARY_DIR}/coverage ) 11 | 12 | # Generate "baseline" coverage data with zero coverage for every instrumented 13 | # line. This is later combined with the coverage data from test run to ensure 14 | # that the percentage of total lines covered is correct even when not all source 15 | # code files were loaded during the test. 16 | execute_process( COMMAND lcov --directory ${CMAKE_BINARY_DIR} 17 | --base-directory ${CMAKE_BINARY_DIR} 18 | --initial 19 | --capture 20 | --rc lcov_branch_coverage=1 21 | --rc genhtml_branch_coverage=1 22 | --output-file=${CMAKE_BINARY_DIR}/base_coverage.info ) 23 | 24 | # Capture all the test binaries. 25 | file( GLOB files "${CMAKE_BINARY_DIR}/bin/tests/*" ) 26 | 27 | # Create an empty report file. 28 | set( REPORT_FILE ${CMAKE_BINARY_DIR}/utest_report.txt ) 29 | file( WRITE ${REPORT_FILE} "" ) 30 | 31 | # Execute all test binaries and capture all the output in the report file. 32 | foreach( testname ${files} ) 33 | get_filename_component( test ${testname} NAME_WLE ) 34 | message( "Running ${testname}" ) 35 | execute_process( COMMAND ${testname} OUTPUT_FILE ${CMAKE_BINARY_DIR}/${test}_out.txt ) 36 | 37 | # Append the test run output to the report file. 38 | file( READ ${CMAKE_BINARY_DIR}/${test}_out.txt CONTENTS ) 39 | file( APPEND ${REPORT_FILE} "${CONTENTS}" ) 40 | endforeach() 41 | 42 | # Generate Junit style xml output. 43 | execute_process( COMMAND ruby 44 | ${UNITY_DIR}/auto/parse_output.rb 45 | -xml ${REPORT_FILE} 46 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) 47 | 48 | # Capture coverage data after test run. 49 | execute_process( COMMAND lcov --capture 50 | --rc lcov_branch_coverage=1 51 | --rc genhtml_branch_coverage=1 52 | --base-directory ${CMAKE_BINARY_DIR} 53 | --directory ${CMAKE_BINARY_DIR} 54 | --output-file ${CMAKE_BINARY_DIR}/second_coverage.info ) 55 | 56 | # Combine baseline coverage data (zeros) with the coverage data from test run. 57 | execute_process( COMMAND lcov --base-directory ${CMAKE_BINARY_DIR} 58 | --directory ${CMAKE_BINARY_DIR} 59 | --add-tracefile ${CMAKE_BINARY_DIR}/base_coverage.info 60 | --add-tracefile ${CMAKE_BINARY_DIR}/second_coverage.info 61 | --output-file ${CMAKE_BINARY_DIR}/coverage.info 62 | --no-external 63 | --rc lcov_branch_coverage=1 ) 64 | 65 | # Generate HTML Report. 66 | execute_process( COMMAND genhtml --rc lcov_branch_coverage=1 67 | --branch-coverage 68 | --output-directory ${CMAKE_BINARY_DIR}/coverage 69 | ${CMAKE_BINARY_DIR}/coverage.info ) 70 | -------------------------------------------------------------------------------- /tools/unity/create_test.cmake: -------------------------------------------------------------------------------- 1 | # Function to create the test executable. 2 | function( create_test_binary_target test_name 3 | test_src 4 | link_list 5 | dep_list 6 | include_list ) 7 | include ( CTest ) 8 | get_filename_component( test_src_absolute ${test_src} ABSOLUTE ) 9 | 10 | # Generate test runner file. 11 | add_custom_command( OUTPUT ${test_name}_runner.c 12 | COMMAND ruby ${UNITY_DIR}/auto/generate_test_runner.rb 13 | ${MODULE_ROOT_DIR}/tools/unity/project.yml 14 | ${test_src_absolute} 15 | ${test_name}_runner.c 16 | DEPENDS ${test_src} ) 17 | 18 | add_executable( ${test_name} ${test_src} ${test_name}_runner.c ) 19 | 20 | set_target_properties( ${test_name} PROPERTIES 21 | COMPILE_FLAG "-O0 -ggdb" 22 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/tests" 23 | INSTALL_RPATH_USE_LINK_PATH TRUE 24 | LINK_FLAGS "-Wl,-rpath,${CMAKE_BINARY_DIR}/lib \ 25 | -Wl,-rpath,${CMAKE_CURRENT_BINARY_DIR}/lib" ) 26 | 27 | target_include_directories( ${test_name} PUBLIC ${mocks_dir} ${include_list} ) 28 | 29 | target_link_directories( ${test_name} PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ) 30 | 31 | # Link all libraries sent through parameters. 32 | foreach( link IN LISTS link_list ) 33 | target_link_libraries( ${test_name} ${link} ) 34 | endforeach() 35 | 36 | # Add dependency to all the dep_list parameter. 37 | foreach( dependency IN LISTS dep_list ) 38 | add_dependencies( ${test_name} ${dependency} ) 39 | target_link_libraries( ${test_name} ${dependency} ) 40 | endforeach() 41 | 42 | target_link_libraries( ${test_name} -lgcov unity ) 43 | target_link_directories( ${test_name} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/lib ) 44 | 45 | add_test( NAME ${test_name} 46 | COMMAND ${CMAKE_BINARY_DIR}/bin/tests/${test_name} 47 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) 48 | endfunction() 49 | 50 | # Function to create target for library under test. 51 | function( create_library_target target_name 52 | src_file 53 | include_list ) 54 | add_library( ${target_name} STATIC ${src_file} ) 55 | target_include_directories( ${target_name} PUBLIC ${include_list} ) 56 | 57 | set_target_properties( ${target_name} PROPERTIES 58 | COMPILE_FLAGS "-Wextra -Wpedantic \ 59 | -fprofile-arcs -ftest-coverage -fprofile-generate \ 60 | -Wno-unused-but-set-variable" 61 | LINK_FLAGS "-fprofile-arcs -ftest-coverage \ 62 | -fprofile-generate " 63 | ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib ) 64 | endfunction() 65 | -------------------------------------------------------------------------------- /tools/unity/project.yml: -------------------------------------------------------------------------------- 1 | :unity: 2 | :when_no_prototypes: :warn 3 | :enforce_strict_ordering: TRUE 4 | :treat_as: 5 | uint8: HEX8 6 | uint16: HEX16 7 | uint32: UINT32 8 | int8: INT8 9 | bool: UINT8 10 | :treat_externs: :exclude 11 | :weak: __attribute__((weak)) 12 | :treat_externs: :include 13 | --------------------------------------------------------------------------------