[修改] 增加freeRTOS

1. 版本FreeRTOSv202212.01,命名为kernel;
This commit is contained in:
2023-05-06 16:43:01 +00:00
commit a345df017b
20944 changed files with 11094377 additions and 0 deletions

View File

@ -0,0 +1,63 @@
# Contributing Guidelines
Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
documentation, we greatly value feedback and contributions from our community.
Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
information to effectively respond to your bug report or contribution.
## Reporting Bugs/Feature Requests
We welcome you to use the GitHub issue tracker to report bugs or suggest features.
When filing an issue, please check [existing open](https://github.com/FreeRTOS/backoffAlgorithm/issues), or [recently closed](https://github.com/FreeRTOS/backoffAlgorithm/issues?q=is%3Aissue+is%3Aclosed), issues to make sure somebody else hasn't already
reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
* A reproducible test case or series of steps
* The version of our code being used
* Any modifications you've made relevant to the bug
* Anything unusual about your environment or deployment
## Contributing via Pull Requests
Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
1. You are working against the latest source on the *main* branch.
1. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
1. You open an issue to discuss any significant work - we would hate for your time to be wasted.
To send us a pull request, please:
1. Fork the repository.
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.
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).
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).
1. Ensure local tests pass.
1. Commit to your fork using clear commit messages.
1. Send us a pull request, answering any default questions in the pull request interface.
1. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
[creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
## Finding contributions to work on
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/FreeRTOS/backoffAlgorithm/labels?q=help+wanted) issues is a great place to start.
## Code of Conduct
This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
opensource-codeofconduct@amazon.com with any additional questions or comments.
## Security issue notifications
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.
## Licensing
See the [LICENSE](../LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
We may ask you to sign a [Contributor License Agreement (CLA)](https://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.

View File

@ -0,0 +1,13 @@
{
"lib_name" : "coreSNTP",
"src": [
"source/core_sntp_client.c",
"source/core_sntp_serializer.c"
],
"include": [
"source/include"
],
"compiler_flags": [
"SNTP_DO_NOT_USE_CUSTOM_CONFIG"
]
}

View File

@ -0,0 +1,174 @@
name: CI Checks
on:
push:
branches: ["**"]
pull_request:
branches: [main]
workflow_dispatch:
jobs:
build-check:
runs-on: ubuntu-latest
steps:
- name: Clone This Repo
uses: actions/checkout@v2
- name: Build Library in Debug mode
run: |
cmake -S test -B build/ \
-G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_C_FLAGS='-O0 -Wall -Wextra -Werror -Wformat -Wformat-security -Warray-bounds'
make -C build/ coverity_analysis -j8
- name: Build Library in Release mode
run: |
rm -rf ./build
cmake -S test -B build/ -G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_FLAGS='-Wall -Wextra -Werror -DNDEBUG -Wformat -Wformat-security -Warray-bounds'
make -C build/ coverity_analysis -j8
build-code-example:
runs-on: ubuntu-latest
steps:
- name: Clone This Repo
uses: actions/checkout@v2
- name: Build Code Example used in Doxygen
run: |
cmake -S test -B Build -DBUILD_CODE_EXAMPLE=ON
make -C Build code_example_posix -j8
unittest-with-sanitizer:
runs-on: ubuntu-latest
steps:
- name: Clone This Repo
uses: actions/checkout@v2
- name: Build Library and Unit Tests with Sanitizer
run: |
CFLAGS="-O0 -Wall -Wexta -Werror"
CFLAGS+=" -D_FORTIFY_SOURCE=2"
CFLAGS+=" -Wformat"
CLFAGS+=" -Wformat-security"
CFLAGS+=" -Warray-bounds"
CFLAGS+=" -fsanitize=address,undefined"
CFLAGS+=" -fsanitize=pointer-compare -fsanitize=pointer-subtract"
CFLAGS+=" -fsanitize-recover=undefined"
CFLAGS+=" -fsanitize-address-use-after-scope"
CFLAGS+=" -fsanitize-undefined-trap-on-error"
CFLAGS=" -fstack-protector-all -DLOGGING_LEVEL_DEBUG=1"
cmake -S test -B build/ \
-G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_UNIT_TESTS=ON \
-DCMAKE_C_FLAGS="${CFLAGS}"
make -C build all -j8
- name: Run unit tests with sanitizer
run: |
cd build
ctest -E system --output-on-failure
cd ..
unittest-for-coverage:
runs-on: ubuntu-latest
steps:
- name: Clone This Repo
uses: actions/checkout@v2
- name: Build
run: |
sudo apt-get install -y lcov sed
# Build with logging enabled.
cmake -S test -B build/ \
-G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_UNIT_TESTS=ON \
-DCMAKE_C_FLAGS='--coverage -Wall -Wextra -Werror -DNDEBUG -Wno-error=pedantic -Wno-variadic-macros -DLOGGING_LEVEL_DEBUG=1'
make -C build/ all
- name: Test
run: |
cd build/
ctest -E system --output-on-failure
cd ..
- name: Run Coverage
run: |
make -C build/ coverage
declare -a EXCLUDE=("\*test\*" "\*CMakeCCompilerId\*" "\*mocks\*")
echo ${EXCLUDE[@]} | xargs lcov --rc lcov_branch_coverage=1 -r build/coverage.info -o build/coverage.info
lcov --rc lcov_branch_coverage=1 --list build/coverage.info
- name: Check Coverage
uses: FreeRTOS/CI-CD-Github-Actions/coverage-cop@main
with:
path: ./build/coverage.info
complexity:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Check complexity
uses: FreeRTOS/CI-CD-Github-Actions/complexity@main
with:
path: ./
spell-check:
runs-on: ubuntu-latest
steps:
- name: Clone This Repo
uses: actions/checkout@v2
- name: Run spellings check
uses: FreeRTOS/CI-CD-Github-Actions/spellings@main
with:
path: ./
formatting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Check formatting
uses: FreeRTOS/CI-CD-Github-Actions/formatting@main
with:
path: ./
exclude-dirs: .git
git-secrets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Checkout awslabs/git-secrets
uses: actions/checkout@v2
with:
repository: awslabs/git-secrets
ref: master
path: git-secrets
- name: Install git-secrets
run: cd git-secrets && sudo make install && cd ..
- name: Run git-secrets
run: |
git-secrets --register-aws
git-secrets --scan
link-verifier:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Python for link verifier action
uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: Check Links
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: FreeRTOS/CI-CD-GitHub-Actions/link-verifier@main
with:
path: ./
exclude-dirs: cbmc
include-file-types: .c,.h,.dox
doxygen:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run doxygen build
uses: FreeRTOS/CI-CD-Github-Actions/doxygen@main
with:
path: ./
memory_statistics:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Python3
uses: actions/setup-python@v2
with:
python-version: '3.7.x'
- name: Measure sizes
uses: FreeRTOS/CI-CD-Github-Actions/memory_statistics@main
with:
config: .github/memory_statistics_config.json
check_against: docs/doxygen/include/size_table.md

View File

@ -0,0 +1,11 @@
name: Doxygen Generation
on:
push:
branches: [main]
workflow_dispatch:
jobs:
doxygen-generation:
runs-on: ubuntu-latest
steps:
- name: Doxygen generation
uses: FreeRTOS/CI-CD-Github-Actions/doxygen-generation@main

View File

@ -0,0 +1,174 @@
name: Release automation
on:
workflow_dispatch:
inputs:
commit_id:
description: 'Commit ID to tag and create a release for'
required: true
version_number:
description: 'Release Version Number (Eg, v1.0.0)'
required: true
delete_existing_tag_release:
description: 'Is this a re-release of existing tag/release? (Default: false)'
default: 'false'
required: false
jobs:
clean-existing-tag-and-release:
if: ${{ github.event.inputs.delete_existing_tag_release == 'true' }}
runs-on: ubuntu-latest
env:
VERSION_NUM: ${{ github.event.inputs.version_number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Check if tag exists
run: |
git fetch origin
if git tag --list $VERSION_NUM
then
echo "Deleting existing tag for $VERSION_NUM"
git push origin --delete tags/$VERSION_NUM
fi
- name: Check if release exists
run: |
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-key 23F3D4EA75716059
sudo apt-add-repository https://cli.github.com/packages
sudo apt update
sudo apt-get install gh
if gh release list | grep $VERSION_NUM
then
echo "Deleting existing release for $VERSION_NUM"
gh release delete --yes $VERSION_NUM
fi
tag-commit:
if: ${{ ( github.event.inputs.delete_existing_tag_release == 'true' && success() ) || ( github.event.inputs.delete_existing_tag_release == 'false' && always() ) }}
needs: clean-existing-tag-and-release
name: Generate SBOM and tag commit
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.commit_id }}
- name: Configure git identity
run: |
git config --global user.name ${{ github.actor }}
git config --global user.email ${{ github.actor }}@users.noreply.github.com
- name: create a new branch that references commit id
run: git checkout -b ${{ github.event.inputs.version_number }} ${{ github.event.inputs.commit_id }}
- name: Generate SBOM
uses: FreeRTOS/CI-CD-Github-Actions/sbom-generator@main
with:
repo_path: ./
source_path: ./source
- name: commit SBOM file
run: |
git add .
git commit -m 'Update SBOM'
git push -u origin ${{ github.event.inputs.version_number }}
- name: Tag Commit and Push to remote
run: |
git tag ${{ github.event.inputs.version_number }} -a -m "coreSNTP Library ${{ github.event.inputs.version_number }}"
git push origin --tags
- name: Verify tag on remote
run: |
git tag -d ${{ github.event.inputs.version_number }}
git remote update
git checkout tags/${{ github.event.inputs.version_number }}
git diff ${{ github.event.inputs.commit_id }} tags/${{ github.event.inputs.version_number }}
create-zip:
if: ${{ ( github.event.inputs.delete_existing_tag_release == 'true' && success() ) || ( github.event.inputs.delete_existing_tag_release == 'false' && always() ) }}
needs: tag-commit
name: Create ZIP and verify package for release asset.
runs-on: ubuntu-latest
steps:
- name: Install ZIP tools
run: sudo apt-get install zip unzip
- name: Checkout code
uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.commit_id }}
path: coreSNTP
submodules: recursive
- name: Checkout disabled submodules
run: |
cd coreSNTP
git submodule update --init --checkout --recursive
- name: Create ZIP
run: |
zip -r coreSNTP-${{ github.event.inputs.version_number }}.zip coreSNTP -x "*.git*"
ls ./
- name: Validate created ZIP
run: |
mkdir zip-check
mv coreSNTP-${{ github.event.inputs.version_number }}.zip zip-check
cd zip-check
unzip coreSNTP-${{ github.event.inputs.version_number }}.zip -d coreSNTP-${{ github.event.inputs.version_number }}
ls coreSNTP-${{ github.event.inputs.version_number }}
diff -r -x "*.git*" coreSNTP-${{ github.event.inputs.version_number }}/coreSNTP/ ../coreSNTP/
cd ../
- name: Build
run: |
cd zip-check/coreSNTP-${{ github.event.inputs.version_number }}/coreSNTP
sudo apt-get install -y lcov
cmake -S test -B build/ \
-G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_UNIT_TESTS=ON \
-DCMAKE_C_FLAGS='--coverage -Wall -Wextra -DNDEBUG'
make -C build/ all
- name: Test
run: |
cd zip-check/coreSNTP-${{ github.event.inputs.version_number }}/coreSNTP/build/
ctest -E system --output-on-failure
cd ..
- name: Create artifact of ZIP
uses: actions/upload-artifact@v2
with:
name: coreSNTP-${{ github.event.inputs.version_number }}.zip
path: zip-check/coreSNTP-${{ github.event.inputs.version_number }}.zip
deploy-doxygen:
needs: tag-commit
name: Deploy doxygen documentation
runs-on: ubuntu-latest
steps:
- name: Doxygen generation
uses: FreeRTOS/CI-CD-Github-Actions/doxygen-generation@main
with:
ref: ${{ github.event.inputs.version_number }}
add_release: "true"
create-release:
needs:
- create-zip
- deploy-doxygen
if: ${{ ( github.event.inputs.delete_existing_tag_release == 'true' && success() ) || ( github.event.inputs.delete_existing_tag_release == 'false' && always() ) }}
name: Create Release and Upload Release Asset
runs-on: ubuntu-latest
steps:
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.event.inputs.version_number }}
release_name: ${{ github.event.inputs.version_number }}
body: Release ${{ github.event.inputs.version_number }} of the coreSNTP Library.
draft: false
prerelease: false
- name: Download ZIP artifact
uses: actions/download-artifact@v2
with:
name: coreSNTP-${{ github.event.inputs.version_number }}.zip
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./coreSNTP-${{ github.event.inputs.version_number }}.zip
asset_name: coreSNTP-${{ github.event.inputs.version_number }}.zip
asset_content_type: application/zip

View File

@ -0,0 +1,13 @@
# Ignore documentation output.
**/docs/**/output/*
# Ignore CMake build directory.
build/
# Ignore build artifacts.
*.o
# Ignore code coverage artifacts.
*.gcda
*.gcno
*.gcov

View File

@ -0,0 +1,4 @@
[submodule "test/unit-test/CMock"]
path = test/unit-test/CMock
url = https://github.com/ThrowTheSwitch/CMock
update = none

View File

@ -0,0 +1,30 @@
path_classifiers:
library:
- exclude: /
extraction:
# Provide build configuration for the C files.
cpp:
prepare:
packages:
- g++
after_prepare:
- export CFLAGS='-Isource/include -Wall -Wextra -Werror -Wformat -Wformat-security -Warray-bounds -DSNTP_DO_NOT_USE_CUSTOM_CONFIG'
index:
build_command:
- g++ $CFLAGS -c source/core_sntp_client.c source/core_sntp_serializer.c
csharp:
after_prepare:
- false
go:
after_prepare:
- false
java:
after_prepare:
- false
javascript:
after_prepare:
- false
python:
after_prepare:
- false

View File

@ -0,0 +1,21 @@
# Changelog for coreSNTP Library
## v1.2.0 (October 2022)
### Changes
- [#63](https://github.com/FreeRTOS/coreSNTP/pull/63) Move user config includes from header to C files.
- [#61](https://github.com/FreeRTOS/coreSNTP/pull/61) MISRA C:2012 compliance update
- [#60](https://github.com/FreeRTOS/coreSNTP/pull/60) Update CBMC Starter kit
- [#57](https://github.com/FreeRTOS/coreSNTP/pull/57) Loop Invariant Update
## v1.1.0 (November 2021)
### Changes
- [#52](https://github.com/FreeRTOS/coreSNTP/pull/52) Change license from MIT-0 to MIT.
- [#47](https://github.com/FreeRTOS/coreSNTP/pull/47) Update doxygen version used for documentation to 1.9.2.
## v1.0.0 (July 2021)
This is the first release of an coreSNTP client library in this repository.
This library implements an SNTP client for the [SNTPv4 specification](https://tools.ietf.org/html/rfc4330). It is optimized for resource-constrained devices, and does not allocate any memory.

View File

@ -0,0 +1,19 @@
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,18 @@
# MISRA Compliance
The coreSNTP library files conform to the [MISRA C:2012](https://www.misra.org.uk)
guidelines, with some noted exceptions. Compliance is checked with Coverity static analysis.
The specific deviations, suppressed inline, are listed below.
Additionally, [MISRA configuration file](https://github.com/FreeRTOS/coreSNTP/blob/main/tools/coverity/misra.config) contains the project wide deviations.
### Suppressed with Coverity Comments
To find the violation references in the source files run grep on the source code
with ( Assuming rule 11.5 violation; with justification in point 1 ):
```
grep 'MISRA Ref 11.5.1' . -rI
```
#### Rule 11.5
_Ref 11.5.1_
- MISRA C-2012 Rule 11.5 Allow casts from `void *`. The library casts the byte
array information received network to a `SntpPacket_t *` for parsing SNTP packet.

View File

@ -0,0 +1,100 @@
## coreSNTP Library
This repository contains the coreSNTP library, a client library to use Simple Network Time Protocol (SNTP) to synchronize device clocks with internet time. This library implements the SNTPv4 specification defined in [RFC 4330](https://tools.ietf.org/html/rfc4330).
An SNTP client can request time from both NTP and SNTP servers. According to the SNTPv4 specification, "_To an NTP or SNTP server, NTP and SNTP clients are indistinguishable; to an NTP or SNTP client, NTP and SNTP servers are indistinguishable._", thereby, allowing SNTP clients to request time from NTP servers.
This library has gone through code quality checks including verification that no function has a [GNU Complexity](https://www.gnu.org/software/complexity/manual/complexity.html) score over 8, and checks against deviations from mandatory rules in the [MISRA coding standard](https://www.misra.org.uk). Deviations from the MISRA C:2012 guidelines are documented under [MISRA Deviations](MISRA.md). This library has also undergone both static code analysis from [Coverity static analysis](https://scan.coverity.com/), and validation of memory safety through the [CBMC automated reasoning tool](https://www.cprover.org/cbmc/).
See memory requirements for this library [here](./docs/doxygen/include/size_table.md).
**coreSNTP v1.2.0 [source code](https://github.com/FreeRTOS/coreSNTP/tree/v1.2.0/source) is part of the [FreeRTOS 202210.00 LTS](https://github.com/FreeRTOS/FreeRTOS-LTS/tree/202210.00-LTS) release.**
### Documentation
The API reference documentation for the coreSNTP library version released in [FreeRTOS/FreeRTOS](https://github.com/FreeRTOS/FreeRTOS) can be viewed from the [freertos.org website](https://freertos.org/coresntp/index.html).
## Cloning this repository
This repo uses [Git Submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to bring in dependent components.
To clone using HTTPS:
```
git clone https://github.com/FreeRTOS/coreSNTP.git --recurse-submodules
```
Using SSH:
```
git clone git@github.com:FreeRTOS/coreSNTP.git --recurse-submodules
```
If you have downloaded the repo without using the `--recurse-submodules` argument, you need to run:
```
git submodule update --init --recursive
```
## Building the library
You can build the coreSNTP source files that are in the [source](source/) directory, and add [source/include](source/include) to your compiler's include path.
If using CMake, the [coreSntpFilePaths.cmake](coreSntpFilePaths.cmake) file contains the above information of the source files and the header include path from this repository.
## Reference Example
A reference example of using the coreSNTP library can be viewed in the `FreeRTOS/FreeRTOS` repository [here](https://github.com/FreeRTOS/FreeRTOS/tree/main/FreeRTOS-Plus/Demo/coreSNTP_Windows_Simulator).
The demo application showcases use of the library in order to create an SNTP client for periodic time synchronization of the system clock.
## Building Unit Tests
The unit tests for the library use CMock/Unity unit testing framework.
### Checkout CMock Submodule
To build unit tests, the submodule dependency of CMock is required. Use the following command to clone the submodule:
```
git submodule update --checkout --init --recursive test/unit-test/CMock
```
### Unit Test Platform Prerequisites
- For running unit tests
- **C90 compiler** like gcc
- **CMake 3.13.0 or later**
- **Ruby 2.0.0 or later** is additionally required for the CMock test framework (that we use).
- For running the coverage target, **gcov** and **lcov** are additionally required.
### Steps to build **Unit Tests**
1. Go to the root directory of this repository. (Make sure that the **CMock** submodule is cloned as described [above](#checkout-cmock-submodule))
1. Run the *cmake* command: `cmake -S test -B build -DBUILD_UNIT_TESTS=ON`
1. Run this command to build the library and unit tests: `make -C build all`
1. The generated test executables will be present in `build/bin/tests` folder.
1. Run `cd build && ctest` to execute all tests and view the test run summary.
## CBMC proofs
To learn more about CBMC and proofs specifically, review the training material [here](https://model-checking.github.io/cbmc-training).
The `test/cbmc/proofs` directory contains CBMC proofs.
In order to run these proofs you will need to install CBMC and other tools by following the instructions [here](https://model-checking.github.io/cbmc-training/installation.html).
## Generating documentation
The Doxygen references were created using Doxygen version 1.9.2. To generate the
Doxygen pages, please run the following command from the root of this repository:
```shell
doxygen docs/doxygen/config.doxyfile
```
## Contributing
See [CONTRIBUTING.md](./.github/CONTRIBUTING.md) for information on contributing.
## License
This library is licensed under the MIT License. See the [LICENSE](LICENSE) file.

View File

@ -0,0 +1,5 @@
## Reporting a Vulnerability
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/) or directly via email to aws-security@amazon.com.
Please do **not** create a public github issue.

View File

@ -0,0 +1,15 @@
# This file is to add source files and include directories
# into variables so that it can be reused from different repositories
# in their Cmake based build system by including this file.
#
# Files specific to the repository such as test runner, platform tests
# are not added to the variables.
# coreSNTP library source files.
set( CORE_SNTP_SOURCES
"${CMAKE_CURRENT_LIST_DIR}/source/core_sntp_serializer.c"
"${CMAKE_CURRENT_LIST_DIR}/source/core_sntp_client.c" )
# coreSNTP library Public Include directories.
set( CORE_SNTP_INCLUDE_PUBLIC_DIRS
"${CMAKE_CURRENT_LIST_DIR}/source/include" )

View File

@ -0,0 +1,272 @@
#include <stdlib.h>
#include <netdb.h>
#include <time.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <poll.h>
#include <string.h>
#include <assert.h>
#include "core_sntp_client.h"
/* @[code_example_sntpdnsresolve] */
/* Example POSIX implementation of SntpDnsReolve_t interface. */
static bool resolveDns( const SntpServerInfo_t * pServerAddr,
uint32_t * pIpV4Addr )
{
bool status = false;
int32_t dnsStatus = -1;
struct addrinfo hints;
struct addrinfo * pListHead = NULL;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = ( int32_t ) SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
dnsStatus = getaddrinfo( pServerAddr->pServerName, NULL, &hints, &pListHead );
if( dnsStatus == 0 )
{
struct sockaddr_in * pAddrInfo = ( struct sockaddr_in * ) pListHead->ai_addr;
inet_ntop( pAddrInfo->sin_family,
&pAddrInfo->sin_addr,
( int8_t * ) pIpV4Addr,
INET_ADDRSTRLEN );
status = true;
}
freeaddrinfo( pListHead );
return status;
}
/* @[code_example_sntpdnsresolve] */
/* @[code_example_networkcontext] */
/* Example definition of NetworkContext_t for UDP socket operations. */
struct NetworkContext
{
int udpSocket;
};
/* @[code_example_networkcontext] */
/* @[code_example_udptransport_sendto] */
/* Example POSIX implementation of the UdpTransportSendTo_t function of UDP transport interface. */
static int32_t UdpTransport_Send( NetworkContext_t * pNetworkContext,
uint32_t serverAddr,
uint16_t serverPort,
const void * pBuffer,
uint16_t bytesToSend )
{
int32_t bytesSent = -1, pollStatus = 1;
struct pollfd pollFds;
pollFds.events = POLLOUT | POLLPRI;
pollFds.revents = 0;
pollFds.fd = pNetworkContext->udpSocket;
/* Check if there is data to read from the socket. */
pollStatus = poll( &pollFds, 1, 0 );
if( pollStatus > 0 )
{
struct sockaddr_in addrInfo;
addrInfo.sin_family = AF_INET;
addrInfo.sin_port = htons( serverPort );
addrInfo.sin_addr.s_addr = htonl( serverAddr );
bytesSent = sendto( pNetworkContext->udpSocket,
pBuffer,
bytesToSend, 0,
( const struct sockaddr * ) &addrInfo,
sizeof( addrInfo ) );
}
else if( pollStatus == 0 )
{
bytesSent = 0;
}
return bytesSent;
}
/* @[code_example_udptransport_sendto] */
/* @[code_example_udptransport_recvfrom] */
/* Example POSIX implementation of the UdpTransportRecvFrom_t function of UDP transport interface. */
static int32_t UdpTransport_Recv( NetworkContext_t * pNetworkContext,
uint32_t serverAddr,
uint16_t serverPort,
void * pBuffer,
uint16_t bytesToRecv )
{
int32_t bytesReceived = -1, pollStatus = 1;
struct pollfd pollFds;
pollFds.events = POLLIN | POLLPRI;
pollFds.revents = 0;
pollFds.fd = pNetworkContext->udpSocket;
/* Check if there is data to read from the socket. */
pollStatus = poll( &pollFds, 1, 0 );
if( pollStatus > 0 )
{
struct sockaddr_in addrInfo;
addrInfo.sin_family = AF_INET;
addrInfo.sin_port = htons( serverPort );
addrInfo.sin_addr.s_addr = htonl( serverAddr );
socklen_t addrLen = sizeof( addrInfo );
bytesReceived = recvfrom( pNetworkContext->udpSocket, pBuffer,
bytesToRecv, 0,
( struct sockaddr * ) &addrInfo,
&addrLen );
}
else if( pollStatus == 0 )
{
bytesReceived = 0;
}
return bytesReceived;
}
/* @[code_example_udptransport_recvfrom] */
/* @[code_example_sntpsettime] */
/* Example implementation of the SntpSetTime_t interface for POSIX platforms. */
static void sntpClient_SetTime( const SntpServerInfo_t * pTimeServer,
const SntpTimestamp_t * pServerTime,
int64_t clockOffsetMs,
SntpLeapSecondInfo_t leapSecondInfo )
{
/* @[code_example_sntp_converttounixtime] */
uint32_t unixSecs;
uint32_t unixMs;
SntpStatus_t status = Sntp_ConvertToUnixTime( pServerTime, &unixSecs, &unixMs );
/* @[code_example_sntp_converttounixtime] */
assert( status == SntpSuccess );
struct timespec serverTime =
{
.tv_sec = unixSecs,
.tv_nsec = unixMs * 1000
};
clock_settime( CLOCK_REALTIME, &serverTime );
}
/* @[code_example_sntpsettime] */
/* @[code_example_sntpgettime] */
/* Example implementation of the SntpGetTime_t interface for POSIX platforms. */
static void sntpClient_GetTime( SntpTimestamp_t * pCurrentTime )
{
struct timespec currTime;
( void ) clock_gettime( CLOCK_REALTIME, &currTime );
pCurrentTime->seconds = currTime.tv_sec;
pCurrentTime->fractions = ( currTime.tv_sec / 1000 ) * SNTP_FRACTION_VALUE_PER_MICROSECOND;
}
/* @[code_example_sntpgettime] */
/* Configuration constants for the example SNTP client. */
/* Following Time Servers are used for illustrating the usage of library API.
* The library can be configured to use ANY time server, whether publicly available
* time service like NTP Pool or a privately owned NTP server. */
#define TEST_TIME_SERVER_1 "0.pool.ntp.org"
#define TEST_TIME_SERVER_2 "1.pool.ntp.org"
#define SERVER_RESPONSE_TIMEOUT_MS 3000
#define TIME_REQUEST_SEND_WAIT_TIME_MS 2000
#define TIME_REQUEST_RECEIVE_WAIT_TIME_MS 1000
#define SYSTEM_CLOCK_FREQUENCY_TOLERANCE_PPM 500
#define SYSTEM_CLOCK_DESIRED_ACCURACY_MS 300
int main( void )
{
/* @[code_example_sntp_init] */
/* Memory for network buffer. */
uint8_t networkBuffer[ SNTP_PACKET_BASE_SIZE ];
/* Create UDP socket. */
NetworkContext_t udpContext;
udpContext.udpSocket = socket( AF_INET, SOCK_DGRAM, 0 );
/* Setup list of time servers. */
SntpServerInfo_t pTimeServers[] =
{
{
.port = SNTP_DEFAULT_SERVER_PORT,
.pServerName = TEST_TIME_SERVER_1,
.serverNameLen = strlen( TEST_TIME_SERVER_1 )
},
{
.port = SNTP_DEFAULT_SERVER_PORT,
.pServerName = TEST_TIME_SERVER_2,
.serverNameLen = strlen( TEST_TIME_SERVER_2 )
}
};
/* Set the UDP transport interface object. */
UdpTransportInterface_t udpTransportIntf;
udpTransportIntf.pUserContext = &udpContext;
udpTransportIntf.sendTo = UdpTransport_Send;
udpTransportIntf.recvFrom = UdpTransport_Recv;
/* Context variable. */
SntpContext_t context;
/* Initialize context. */
SntpStatus_t status = Sntp_Init( &context,
pTimeServers,
sizeof( pTimeServers ) / sizeof( SntpServerInfo_t ),
SERVER_RESPONSE_TIMEOUT_MS,
networkBuffer,
SNTP_PACKET_BASE_SIZE,
resolveDns,
sntpClient_GetTime,
sntpClient_SetTime,
&udpTransportIntf,
NULL );
assert( status == SntpSuccess );
/* @[code_example_sntp_init] */
/* Calculate the polling interval period for the SNTP client. */
/* @[code_example_sntp_calculatepollinterval] */
uint32_t pollingIntervalPeriod;
status = Sntp_CalculatePollInterval( SYSTEM_CLOCK_FREQUENCY_TOLERANCE_PPM,
SYSTEM_CLOCK_DESIRED_ACCURACY_MS,
&pollingIntervalPeriod );
/* @[code_example_sntp_calculatepollinterval] */
assert( status == SntpSuccess );
/* Loop of SNTP client for period time synchronization. */
/* @[code_example_sntp_send_receive] */
while( 1 )
{
status = Sntp_SendTimeRequest( &context,
rand() % UINT32_MAX,
TIME_REQUEST_SEND_WAIT_TIME_MS );
assert( status == SntpSuccess );
do
{
status = Sntp_ReceiveTimeResponse( &context, TIME_REQUEST_RECEIVE_WAIT_TIME_MS );
} while( status == SntpNoResponseReceived );
assert( status == SntpSuccess );
/* Delay of poll interval period before next time synchronization. */
sleep( pollingIntervalPeriod );
}
/* @[code_example_sntp_send_receive] */
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,25 @@
<table>
<tr>
<td colspan="3"><center><b>Code Size of coreSNTP (example generated with GCC for ARM Cortex-M)</b></center></td>
</tr>
<tr>
<td><b>File</b></td>
<td><b><center>With -O1 Optimization</center></b></td>
<td><b><center>With -Os Optimization</center></b></td>
</tr>
<tr>
<td>core_sntp_client.c</td>
<td><center>1.5K</center></td>
<td><center>1.2K</center></td>
</tr>
<tr>
<td>core_sntp_serializer.c</td>
<td><center>1.0K</center></td>
<td><center>0.8K</center></td>
</tr>
<tr>
<td><b>Total estimates</b></td>
<td><b><center>2.5K</center></b></td>
<td><b><center>2.0K</center></b></td>
</tr>
</table>

View File

@ -0,0 +1,228 @@
<doxygenlayout version="1.0">
<!-- Generated by doxygen 1.8.20 -->
<!-- Navigation index tabs for HTML output -->
<navindex>
<tab type="mainpage" visible="yes" title=""/>
<tab type="pages" visible="yes" title="" intro=""/>
<!-- Hide the default "Data Structures" tab and use the "Modules" tab for data
structures. This allows internal data structures to be hidden. -->
<tab type="modules" visible="yes" title="Data types and Constants" intro="This library defines the following data types and constants."/>
<tab type="namespaces" visible="yes" title="">
<tab type="namespacelist" visible="yes" title="" intro=""/>
<tab type="namespacemembers" visible="yes" title="" intro=""/>
</tab>
<tab type="interfaces" visible="no" title="">
<tab type="interfacelist" visible="no" title="" intro=""/>
<tab type="interfaceindex" visible="no" title=""/>
<tab type="interfacehierarchy" visible="yes" title="" intro=""/>
</tab>
<tab type="classes" visible="no" title="">
<tab type="classlist" visible="no" title="" intro=""/>
<tab type="classindex" visible="no" title=""/>
<tab type="hierarchy" visible="no" title="" intro=""/>
<tab type="classmembers" visible="no" title="" intro=""/>
</tab>
<tab type="structs" visible="no" title="">
<tab type="structlist" visible="no" title="" intro=""/>
<tab type="structindex" visible="no" title=""/>
</tab>
<tab type="exceptions" visible="no" title="">
<tab type="exceptionlist" visible="no" title="" intro=""/>
<tab type="exceptionindex" visible="no" title=""/>
<tab type="exceptionhierarchy" visible="yes" title="" intro=""/>
</tab>
<tab type="files" visible="no" title="">
<tab type="filelist" visible="yes" title="Files" intro="The following files are associated with this library."/>
<tab type="globals" visible="no" title="" intro=""/>
</tab>
<tab type="examples" visible="yes" title="" intro=""/>
</navindex>
<!-- Layout definition for a class page -->
<class>
<briefdescription visible="yes"/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<inheritancegraph visible="$CLASS_GRAPH"/>
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
<memberdecl>
<nestedclasses visible="yes" title=""/>
<publictypes title=""/>
<services title=""/>
<interfaces title=""/>
<publicslots title=""/>
<signals title=""/>
<publicmethods title=""/>
<publicstaticmethods title=""/>
<publicattributes title=""/>
<publicstaticattributes title=""/>
<protectedtypes title=""/>
<protectedslots title=""/>
<protectedmethods title=""/>
<protectedstaticmethods title=""/>
<protectedattributes title=""/>
<protectedstaticattributes title=""/>
<packagetypes title=""/>
<packagemethods title=""/>
<packagestaticmethods title=""/>
<packageattributes title=""/>
<packagestaticattributes title=""/>
<properties title=""/>
<events title=""/>
<privatetypes title=""/>
<privateslots title=""/>
<privatemethods title=""/>
<privatestaticmethods title=""/>
<privateattributes title=""/>
<privatestaticattributes title=""/>
<friends title=""/>
<related title="" subtitle=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
<enums title=""/>
<services title=""/>
<interfaces title=""/>
<constructors title=""/>
<functions title=""/>
<related title=""/>
<variables title=""/>
<properties title=""/>
<events title=""/>
</memberdef>
<allmemberslink visible="yes"/>
<usedfiles visible="$SHOW_USED_FILES"/>
<authorsection visible="yes"/>
</class>
<!-- Layout definition for a namespace page -->
<namespace>
<briefdescription visible="yes"/>
<memberdecl>
<nestednamespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<interfaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<structs visible="yes" title=""/>
<exceptions visible="yes" title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdef>
<authorsection visible="yes"/>
</namespace>
<!-- Layout definition for a file page -->
<file>
<briefdescription visible="yes"/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<includegraph visible="$INCLUDE_GRAPH"/>
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
<sourcelink visible="yes"/>
<memberdecl>
<interfaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<structs visible="yes" title=""/>
<exceptions visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<defines title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<functions title=""/>
<variables title=""/>
</memberdef>
<authorsection/>
</file>
<!-- Layout definition for a group page -->
<group>
<briefdescription visible="yes"/>
<groupgraph visible="$GROUP_GRAPHS"/>
<memberdecl>
<nestedgroups visible="yes" title=""/>
<dirs visible="yes" title=""/>
<files visible="yes" title=""/>
<namespaces visible="yes" title=""/>
<classes visible="yes" title=""/>
<defines title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<enumvalues title=""/>
<functions title=""/>
<variables title=""/>
<signals title=""/>
<publicslots title=""/>
<protectedslots title=""/>
<privateslots title=""/>
<events title=""/>
<properties title=""/>
<friends title=""/>
<membergroups visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
<memberdef>
<pagedocs/>
<inlineclasses title=""/>
<defines title=""/>
<typedefs title=""/>
<sequences title=""/>
<dictionaries title=""/>
<enums title=""/>
<enumvalues title=""/>
<functions title=""/>
<variables title=""/>
<signals title=""/>
<publicslots title=""/>
<protectedslots title=""/>
<privateslots title=""/>
<events title=""/>
<properties title=""/>
<friends title=""/>
</memberdef>
<authorsection visible="yes"/>
</group>
<!-- Layout definition for a directory page -->
<directory>
<briefdescription visible="yes"/>
<directorygraph visible="yes"/>
<memberdecl>
<dirs visible="yes"/>
<files visible="yes"/>
</memberdecl>
<detaileddescription title=""/>
</directory>
</doxygenlayout>

View File

@ -0,0 +1,157 @@
/**
@mainpage Overview
@anchor sntp
@brief <b>coreSNTP Library</b>
The coreSNTP library provides a client for the <b>Simple Network Time Protocol (SNTP)</b> to allow devices to synchronize their system clocks with internet time. This library implements the SNTPv4 specification defined in [RFC 4330](https://tools.ietf.org/html/rfc4330).
<p>
An SNTP client can request time from both NTP and SNTP servers. According to the SNTPv4 specification,
> <i>To an NTP or SNTP server, NTP and SNTP clients are indistinguishable; to an NTP or SNTP client, NTP and SNTP servers are indistinguishable.</i>
<span style="float:right;margin-right:4em"> &mdash; <i>[RFC4330](https://tools.ietf.org/html/rfc4330)</i></span><br>
This library is optimized for resource constrained embedded devices. Features of this library include:
- Fully synchronous API, to allow applications to completely manage their concurrency and multi-threading method.
- Operations on fixed buffers, so that applications may control their memory allocation strategy.
- Flexibility to develop an application with either:
- The <b>serializer/de-serializer API</b> which allows complete control over the operations of the SNTP client.
OR
- The <b>client API</b> that handles operations of communicating over the network, performing authentication, and handling rejected server responses.
</p>
@section sntp_memory_requirements Memory Requirements
@brief Memory requirements of the coreSNTP library.
@include{doc} size_table.md
@section sntp_design Design
@brief <b>coreSNTP Library Design</b>
<p>
The coreSNTP library provides 2 API layers:
1. <b>Serializer/Deserializer and Utilities</b> - This layer provides functionality for serializing SNTP time requests and deserializing SNTP response packets, as well as some utility functions helpful in setting up an SNTP client in an application.
2. <b>Client</b> - This layer provides **managed** functionality for network operations including DNS resolution, sending and receiving SNTP packets over UDP, authenticating servers for security (if enabled), and handling of
server rejection of time requests.
**Note**: This <b>client API</b> layer performs network and authentication operations through user-defined implementation of interfaces exposed by the library.
Following is the architecture diagram of the library. It showcases the 2 tiers of the library and the interfaces on which the **Managed Client** layer depends on.
\image html Dependency_Architecture_Diagram.png
@section mqtt_interfaces Interfaces and Callbacks
As visible in the above architecture diagram, the <b>Managed Client</b> API layer depends on the following interfaces that are user-defined for platform-specific operations:
- <b>[DNS Resolve Interface](@ref SntpResolveDns_t) </b> - This allows library to resolve DNS addresses of time servers by the application.
- <b>[UDP Transport Interface](@ref UdpTransportInterface_t) </b> - This allows library to send and receive SNTP packets over network UDP layer to communicate with SNTP/NTP servers.
- <b>[Get Time Interface](@ref SntpGetTime_t)</b> - This allows library to obtain system time for tracking timeouts as well as creating SNTP time requests.
- <b>[Set Time Interface](@ref SntpSetTime_t)</b> - This allows library to notify application about updating system time with the latest time provided by server as well as the system clock drift calculated by library.
- <b>[Authentication Interface](@ref SntpAuthenticationInterface_t) (Optional)</b> - This allows library to perform mutual authentication in communicating with time servers for security. It is RECOMMENDED that applications enable authentication when communicating with time servers.
</p>
*/
/**
@page core_sntp_config Configurations
@brief Configurations of the coreSNTP Library.
<!-- @par configpagestyle allows the @section titles to be styled according to style.css -->
@par configpagestyle
All the configurations settings for the coreSNTP library are function-like macros for logging. They can be set with a `\#define` in the config file (`core_sntp_config.h`) or by using a compiler option such as -D in gcc.
@section SNTP_DO_NOT_USE_CUSTOM_CONFIG
@copydoc SNTP_DO_NOT_USE_CUSTOM_CONFIG
@section sntp_logerror LogError
@copydoc LogError
@section sntp_logwarn LogWarn
@copydoc LogWarn
@section sntp_loginfo LogInfo
@copydoc LogInfo
@section sntp_logdebug LogDebug
@copydoc LogDebug
*/
/**
@page sntp_functions Functions
@brief Primary functions of the SNTP library:<br><br>
@subpage sntp_init_function <br>
@subpage sntp_sendtimerequest_function <br>
@subpage sntp_receivetimeresponse_function <br>
@subpage sntp_serializerequest_function <br>
@subpage sntp_deserializeresponse_function <br>
@subpage sntp_calculatepollinterval_function <br>
@subpage sntp_converttounixtime_function <br>
@page sntp_init_function Sntp_Init
@snippet core_sntp_client.h define_sntp_init
@copydoc Sntp_Init
For an example POSIX application of using the coreSNTP library APIs to create an SNTP client, refer to @ref code_example.
Below is the part of the code example relevant to the @ref Sntp_Init API.
@snippet example_sntp_client_posix.c code_example_sntp_init
@page sntp_sendtimerequest_function Sntp_SendTimeRequest
@copydoc Sntp_SendTimeRequest
For an example POSIX application of using the coreSNTP library APIs to create an SNTP client, refer to @ref code_example.
Below is the part of the code example relevant to the @ref Sntp_ReceiveTimeResponse API.
@snippet example_sntp_client_posix.c code_example_sntp_send_receive
@page sntp_receivetimeresponse_function Sntp_ReceiveTimeResponse
@copydoc Sntp_ReceiveTimeResponse
For an example POSIX application of the coreSNTP library APIs to create an SNTP client, refer to @ref code_example.
Below is the part of the example application relevant to the @ref Sntp_ReceiveTimeResponse API.
@snippet example_sntp_client_posix.c code_example_sntp_send_receive
@page sntp_serializerequest_function Sntp_SerializeRequest
@copydoc Sntp_SerializeRequest
@page sntp_deserializeresponse_function Sntp_DeserializeResponse
@copydoc Sntp_DeserializeResponse
@page sntp_calculatepollinterval_function Sntp_CalculatePollInterval
@copydoc Sntp_CalculatePollInterval
Here is a code example of using the API.
@snippet example_sntp_client_posix.c code_example_sntp_calculatepollinterval
@page sntp_converttounixtime_function Sntp_ConvertToUnixTime
@copydoc Sntp_ConvertToUnixTime
Here is a code example of using the API.
@snippet example_sntp_client_posix.c code_example_sntp_converttounixtime
*/
/**
@page code_example Example application of using coreSNTP APIs
@brief Example application of setting up an SNTP client with coreSNTP Library on POSIX platforms.
This code ONLY showcases how coreSNTP library APIs can be used. For best practices on running an SNTP client,
refer to the [FreeRTOS Demo on GitHub](https://github.com/FreeRTOS/FreeRTOS/tree/main/FreeRTOS-Plus/Demo/coreSNTP_Windows_Simulator).<br>
@include example_sntp_client_posix.c
*/
<!-- We do not use doxygen ALIASes here because there have been issues in the past versions with "^^" newlines within the alias definition. -->
/**
@defgroup sntp_enum_types Enumerated Types
@brief Enumerated types of the coreSNTP library
*/
/**
@defgroup sntp_callback_types Callback Types
@brief Callback function pointer types of the coreSNTP library
*/
/**
@defgroup sntp_struct_types Struct Types
@brief Struct types of the coreSNTP library
*/
/**
@defgroup sntp_constants Constants
@brief Constants defined in the coreSNTP library
*/

View File

@ -0,0 +1,118 @@
/**
@page sntp_porting Porting Guide
@brief Guide for porting coreSNTP library to a new platform.
A port of coreSNTP library for a new platform must provide the following components:
1. [Logging Configuration Macros](@ref sntp_porting_config)
2. [DNS Resolve Function](@ref sntp_porting_dnsresolve)
3. [UDP Transport Interface](@ref sntp_porting_transport)
4. [Get Time Function](@ref sntp_porting_gettime)
5. [Set Time Function](@ref sntp_porting_settime)
6. [Authentication Interface](@ref sntp_porting_authentication)
@section sntp_porting_config Configuration Macros for Logging
@brief Macros for enabling logging that can be defined through the config header `core_sntp_config.h`, or passed in as compiler options.
@note If a custom configuration header `core_sntp_config.h` is not provided, then the @ref SNTP_DO_NOT_USE_CUSTOM_CONFIG macro must be defined.
@see [Configurations](@ref core_sntp_config)
The following logging macros are used throughout the library:
- @ref LogError
- @ref LogWarn
- @ref LogInfo
- @ref LogDebug
Here is an example implementation of logging macros for POSIX platforms
@snippet core_sntp_config.h code_example_loggingmacros
@section sntp_porting_dnsresolve DNS Resolve Function
@brief The coreSNTP library requires a DNS Resolve interface that must be implemented to obtain the latest IPv4
address information of a time server before sending it a time request.
@see [DNS Resolve Function](@ref SntpResolveDns_t)
@note The coreSNTP library will re-resolve the DNS name of a time server on each attempt of requesting time from it.
For efficiency of DNS resolution operations, your implementation can utilize DNS caching of resolved domains if
your platform supports it.
@snippet example_sntp_client_posix.c code_example_sntpdnsresolve
@section sntp_porting_transport UDP Transport Interface
@brief The coreSNTP library requires a UDP transport interface that must be implemented
in order to send and receive SNTP packets over the network.
@see [UDP Transport Interface](@ref UdpTransportInterface_t)
@note For security against unwanted server response packets, it is RECOMMENDED that the UDP socket that is used
for implementing the UDP transport interface functions of performing network I/O is kept open ONLY during
duration of an SNTP request-response iteration as opposed to keeping it always open across iterations. One way to achieve this is to open a new UDP socket before calling @ref Sntp_SendTimeRequest API and close it after receiving server response (or timeout) with the @ref Sntp_ReceiveTimeResponse API.
A port must implement functions corresponding to the following functions pointers:
- [UDP Transport Send](@ref UdpTransportSendTo_t): A function to send bytes on the network over UDP. It is RECOMMENDED to implement this function as non-blocking so the total block time can be managed by the @ref Sntp_SendTimeRequest API.
@snippet example_sntp_client_posix.c code_example_udptransport_sendto
- [UDP Transport Receive](@ref UdpTransportRecvFrom_t): A function to receive bytes from the network over UDP. It is RECOMMENDED to implement this function as non-blocking so the total block time can be managed by the @ref Sntp_ReceiveTimeResponse API.
@snippet example_sntp_client_posix.c code_example_udptransport_recvfrom
The above two functions take in a pointer to a @ref NetworkContext_t, the typename of a
`struct NetworkContext`. The NetworkContext struct must also be defined by the port, and
ought to contain any information necessary to send and receive data with the @ref UdpTransportSendTo_t
and @ref UdpTransportRecvFrom_t implementations, respectively:
@snippet example_sntp_client_posix.c code_example_networkcontext
@section sntp_porting_gettime Get Time Function
@brief The coreSNTP library uses this function to obtain time from system for tracking timeout
durations as well as generating SNTP request packet.
@see @ref SntpGetTime_t
If the device does not have real-world time information (on device boot-up for example), it is
acceptable for this function to provide the system-time that does not match the real-world time, because once a
time information is received from a time server, the system time can be corrected to match the real-world time.
Refer to the next section on how to correct the system time.
@snippet example_sntp_client_posix.c code_example_sntpgettime
@section sntp_porting_settime Set Time Function
@brief The coreSNTP library calls this function to notify the device about the latest time received
from a time server as well as the clock drift of the system time from the server time.
@snippet example_sntp_client_posix.c code_example_sntpsettime
@see @ref SntpSetTime_t
Platforms should implement this function to perform clock disciple operation on the system clock, that is appropriate for
the clock accuracy needs of the application.
@section sntp_porting_authentication Authentication Interface
@brief The coreSNTP library exposes an authentication interface to allow customer-chosen authentication mechanism to be used
in SNTP communication with time server(s) for security.
@note It is RECOMMENDED to enable authentication in communication with your time server(s) of choice to protect against attacks
that modify or spoof server responses. The SNTPv4 protocol is flexible to be used with any symmetric-key or asymmetric key
cryptographic algorithm depending on the support provided by time servers of your choice. For an example of using AES-128-CMAC
as the authentication algorithm, please refer to [coreSNTP demo in FreeRTOS/FreeRTOS repository](https://github.com/FreeRTOS/FreeRTOS/tree/main/FreeRTOS-Plus/Demo/coreSNTP_Windows_Simulator).
@see @ref SntpAuthenticationInterface_t
A port that uses authentication to communicate with time server must implement the following function pointers:
- [Add client authentication code](@ref SntpGenerateAuthCode_t): A function to generate and append authentication data for client to be validated
by the time server. The first @ref SNTP_PACKET_BASE_SIZE bytes in the buffer supplied to this function contains the SNTP request data which can be used to
generate the authentication code. The generated authentication code SHOULD be written to the same buffer after the first @ref SNTP_PACKET_BASE_SIZE bytes.
This function should also return the number of authentication bytes appended to the library through an output parameter, so that the library knows about the
total size of the SNTP packet.
- [Validate server authentication](@ref SntpValidateServerAuth_t): A function to validate the authentication code in a received SNTP time response from the
network to confirm that the expected server is the sender of the response and the timestamps in the packet are trustworthy to
update system time. This server authentication data is usually validated by checking that the data can be regenerated by the client from
the first #SNTP_PACKET_BASE_SIZE bytes of the received SNTP packet from the network.
The above two functions take in a pointer to a @ref SntpAuthContext_t, the typename of a `struct SntpAuthContext`. The `SntpAuthContext` struct must
also be defined by the port, to store necessary information (like a PKCS#11 label representing credential secret) for performing cryptographic generation
and validation operations in the @ref SntpGenerateAuthCode_t and @ref SntpValidateServerAuth_t functions respectively.
*/

View File

@ -0,0 +1,132 @@
/*
* Stylesheet for Doxygen HTML output.
*
* This file defines styles for custom elements in the header/footer and
* overrides some of the default Doxygen styles.
*
* Styles in this file do not affect the treeview sidebar.
*/
/* Set the margins to place a small amount of whitespace on the left and right
* side of the page. */
div.contents {
margin-left:4em;
margin-right:4em;
}
/* Justify text in paragraphs. */
p {
text-align: justify;
}
/* Style of section headings. */
h1 {
border-bottom: 1px solid #879ECB;
color: #354C7B;
font-size: 160%;
font-weight: normal;
padding-bottom: 4px;
padding-top: 8px;
}
/* Style of subsection headings. */
h2:not(.memtitle):not(.groupheader) {
font-size: 125%;
margin-bottom: 0px;
margin-top: 16px;
padding: 0px;
}
/* Style of paragraphs immediately after subsection headings. */
h2 + p {
margin: 0px;
padding: 0px;
}
/* Style of subsection headings. */
h3 {
font-size: 100%;
margin-bottom: 0px;
margin-left: 2em;
margin-right: 2em;
}
/* Style of paragraphs immediately after subsubsection headings. */
h3 + p {
margin-top: 0px;
margin-left: 2em;
margin-right: 2em;
}
/* Style of the prefix "AWS IoT Device SDK C" that appears in the header. */
#csdkprefix {
color: #757575;
}
/* Style of the "Return to main page" link that appears in the header. */
#returntomain {
padding: 0.5em;
}
/* Style of the dividers on Configuration Settings pages. */
div.configpagedivider {
margin-left: 0px !important;
margin-right: 0px !important;
margin-top: 20px !important;
}
/* Style of configuration setting names. */
dl.section.user ~ h1 {
border-bottom: none;
color: #000000;
font-family: monospace, fixed;
font-size: 16px;
margin-bottom: 0px;
margin-left: 2em;
margin-top: 1.5em;
}
/* Style of paragraphs on a configuration settings page. */
dl.section.user ~ * {
margin-bottom: 10px;
margin-left: 4em;
margin-right: 4em;
margin-top: 0px;
}
/* Hide the configuration setting marker. */
dl.section.user {
display: none;
}
/* Overrides for code fragments and lines. */
div.fragment {
background: #ffffff;
border: none;
padding: 5px;
}
div.line {
color: #3a3a3a;
}
/* Overrides for code syntax highlighting colors. */
span.comment {
color: #008000;
}
span.keyword, span.keywordtype, span.keywordflow {
color: #0000ff;
}
span.preprocessor {
color: #50015a;
}
span.stringliteral, span.charliteral {
color: #800c0c;
}
a.code, a.code:visited, a.line, a.line:visited {
color: #496194;
}

View File

@ -0,0 +1,262 @@
actualabsdiff
aes
alarmservernotsynchronized
api
apis
ascii
auth
authcodesize
authintf
aws
backoff
beforelooptime
blocktimems
br
buffersize
bytesorerror
bytesremaining
bytessent
bytestorecv
bytestorecv
bytestosend
bytestosend
cbmc
clienttime
clienttimesec
clienttxtime
clockfreqtolerance
clockoffsetms
cmac
com
config
configpagestyle
configs
const
copydoc
coresntp
coverity
css
currenttimelist
de
deamon
december
defgroup
deserialization
deserialize
deserializer
deserializeresponse
deserializing
desiredaccuracy
diff
dns
doxygen
endcode
endian
endif
enum
expectedbytestosend
expectedinterval
expectedtxtime
faqs
feb
firstorderdiff
fracs
fracsinnetorder
fracsinnetorder
freertos
gcc
getsystemtimefunc
getsystemtimefunc
gettimefunc
github
gov
hsm
html
htonl
https
ietf
ifdef
ifndef
inc
ingroup
inlooptime
int
iot
ip
iso
jan
january
june
kod
lastrequesttime
leapsecondinfo
leapversionmode
logdebug
logerror
loginfo
logwarn
lsb
mainpage
md
mdash
misra
mit
mtu
networkcontext
networkinterfacereceivestub
networkinterfacesendstub
nist
noleapsecond
noninfringement
ntp
ntpv
numofservers
oldertime
org
origintime
overflown
packetsize
param
pauthcodesize
pauthcodesize
pauthintf
pauthintf
pbuffer
pclientrxtime
pclienttime
pclienttxtime
pclockoffset
pcontext
pcurrenttime
phasresponsetimedout
pkcs
pml
pnetworkbuffer
pnetworkbuffer
pnetworkcontext
pnetworkcontext
pnetworkintf
png
poldertime
positivediff
posix
ppacket
pparsedresponse
ppm
ppollinterval
preadstarttime
prequestpacket
prequesttime
prequesttxtime
presponsebuffer
presponsecode
presponsedata
presponsepacket
presponserxtime
printf
pserveraddr
pservername
pserverrxtime
pservertime
pservertxtime
psntptime
ptime
ptimeserver
ptimeservers
ptr
ptransportintf
pudptransportintf
punixtimemicrosecs
punixtimesecs
pusercontext
pwordmemory
randomnum
randomnumber
receivetime
recv
recvfrom
refid
reftime
rejectedresponsecode
resolvednsfunc
responsesize
responsetimeoutms
retryable
rfc
rootdelay
rootdisp
rootdispersion
rstr
rx
sdk
secsinnetorder
secsinnetorder
sendsntppacket
sendto
serializerequest
serveraddr
servernamelen
serverport
serverresponsetimeoutms
servertime
servertimesec
servertxtime
setsystemtimeatindex
setsystemtimefunc
signedfirstorderdiffrecv
signedfirstorderdiffsend
sizeof
sntp
sntpauthcontext
sntpbuffertoosmall
sntpclockoffsetoverflow
sntperrorautherror
sntperrorauthfailure
sntperrorbadparameter
sntperrorbuffertoosmall
sntperrorchangeserver
sntperrorcontextnotinitialized
sntperrordnsfailure
sntperrornetworkfailure
sntperrorresponsetimeout
sntperrorsendtimeout
sntperrortimenotsupported
sntpinvalidresponse
sntpnoresponse
sntpnoresponsereceived
sntppacketsize
sntprejectedresponse
sntprejectedresponsechangeserver
sntprejectedresponseothercode
sntprejectedresponseretrywithbackoff
sntpservernotauthenticated
sntpsuccess
sntptimestamp
sntpv
sntpzeropollinterval
spdx
startingpos
startingpos
struct
sublicense
testbuffer
testclockoffsetcalculation
timebeforeloop
timediffsec
timeoutms
timeserver
transmittime
trng
tx
typename
udp
udprecvretcodes
udpsendretcodes
uint
unix
utc
validateserverauth
wordmemory
wordval
www
xffff

View File

@ -0,0 +1,5 @@
name : "coreSNTP"
version: "v1.2.0"
description: |
"SNTP client library to allow embedded devices to synchronize time with time servers.\n"
license: "MIT"

View File

@ -0,0 +1,36 @@
SPDXVersion: SPDX-2.2
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: coreSNTP
DocumentNamespace: https://github.com/FreeRTOS/coreSNTP/blob/v1.2.0/sbom.spdx
Creator: Amazon Web Services
Created: 2022-10-14T20:34:14Z
CreatorComment: NOASSERTION
DocumentComment: NOASSERTION
PackageName: coreSNTP
SPDXID: SPDXRef-Package-coreSNTP
PackageVersion: v1.2.0
PackageDownloadLocation: https://github.com/FreeRTOS/coreSNTP/tree/v1.2.0
PackageLicenseConcluded: MIT
FilesAnalyzed: True
PackageVerificationCode: ab422c2e6c223d05b532b98e28c94f6c49ccaba0
PackageCopyrightText: NOASSERTION
PackageSummary: NOASSERTION
PackageDescription: "SNTP client library to allow embedded devices to synchronize time with time servers.\n"
FileName: ./core_sntp_serializer.c
SPDXID: SPDXRef-File-core_sntp_serializer.c
FileChecksum: SHA1: 7106d519417e32fdb3a26a5e03410e855e2f5ce0
LicenseConcluded: MIT
FileCopyrightText: NOASSERTION
FileComment: NOASSERTION
FileName: ./core_sntp_client.c
SPDXID: SPDXRef-File-core_sntp_client.c
FileChecksum: SHA1: 9f1b5c57812deb30376ab76e11d09e81ce09c2fe
LicenseConcluded: MIT
FileCopyrightText: NOASSERTION
FileComment: NOASSERTION

View File

@ -0,0 +1,959 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_client.c
* @brief Implementation of the client API of the coreSNTP library.
*/
/* Standard includes. */
#include <assert.h>
#include <string.h>
/* SNTP client library API include. */
#include "core_sntp_client.h"
#include "core_sntp_config_defaults.h"
/**
* @brief Utility to convert fractions part of SNTP timestamp to milliseconds.
*
* @param[in] fractions The fractions value in an SNTP timestamp.
*/
#define FRACTIONS_TO_MS( fractions ) \
( fractions / ( SNTP_FRACTION_VALUE_PER_MICROSECOND * 1000U ) )
SntpStatus_t Sntp_Init( SntpContext_t * pContext,
const SntpServerInfo_t * pTimeServers,
size_t numOfServers,
uint32_t serverResponseTimeoutMs,
uint8_t * pNetworkBuffer,
size_t bufferSize,
SntpResolveDns_t resolveDnsFunc,
SntpGetTime_t getSystemTimeFunc,
SntpSetTime_t setSystemTimeFunc,
const UdpTransportInterface_t * pTransportIntf,
const SntpAuthenticationInterface_t * pAuthIntf )
{
SntpStatus_t status = SntpSuccess;
/* Validate pointer parameters are not NULL. */
if( ( pContext == NULL ) || ( pTimeServers == NULL ) ||
( pNetworkBuffer == NULL ) || ( resolveDnsFunc == NULL ) ||
( getSystemTimeFunc == NULL ) || ( setSystemTimeFunc == NULL ) ||
( pTransportIntf == NULL ) )
{
LogError( ( "Invalid parameter: Pointer parameters (except pAuthIntf) cannot be NULL" ) );
status = SntpErrorBadParameter;
}
/* Validate the length of the servers list.*/
else if( numOfServers == 0U )
{
LogError( ( "Invalid parameter: Size of server list cannot be zero" ) );
status = SntpErrorBadParameter;
}
/* Validate that the UDP transport interface functions are valid. */
else if( ( pTransportIntf->recvFrom == NULL ) || ( pTransportIntf->sendTo == NULL ) )
{
LogError( ( "Invalid parameter: Function members of UDP transport interface cannot be NULL" ) );
status = SntpErrorBadParameter;
}
/* If an authentication interface is provided, validate that its function pointer
* members are valid. */
else if( ( pAuthIntf != NULL ) &&
( ( pAuthIntf->generateClientAuth == NULL ) ||
( pAuthIntf->validateServerAuth == NULL ) ) )
{
LogError( ( "Invalid parameter: Function members of authentication interface cannot be NULL" ) );
status = SntpErrorBadParameter;
}
else if( bufferSize < SNTP_PACKET_BASE_SIZE )
{
LogError( ( "Cannot initialize context: Passed network buffer size is less than %u bytes: "
"bufferSize=%lu", SNTP_PACKET_BASE_SIZE, ( unsigned long ) bufferSize ) );
status = SntpErrorBufferTooSmall;
}
else
{
/* Reset the context memory to zero. */
( void ) memset( pContext, 0, sizeof( SntpContext_t ) );
/* Set the members of the context with passed parameters. */
pContext->pTimeServers = pTimeServers;
pContext->numOfServers = numOfServers;
pContext->responseTimeoutMs = serverResponseTimeoutMs;
pContext->pNetworkBuffer = pNetworkBuffer;
pContext->bufferSize = bufferSize;
pContext->resolveDnsFunc = resolveDnsFunc;
pContext->getTimeFunc = getSystemTimeFunc;
pContext->setTimeFunc = setSystemTimeFunc;
/* Copy contents of UDP transport interface to context. */
( void ) memcpy( &pContext->networkIntf, pTransportIntf, sizeof( UdpTransportInterface_t ) );
/* If authentication interface has been passed, copy its contents to the context. */
if( pAuthIntf != NULL )
{
( void ) memcpy( &pContext->authIntf, pAuthIntf, sizeof( SntpAuthenticationInterface_t ) );
}
/* Initialize the packet size member to the standard minimum SNTP packet size.*/
pContext->sntpPacketSize = SNTP_PACKET_BASE_SIZE;
}
return status;
}
/**
* @brief Utility to calculate the difference in milliseconds between 2
* SNTP timestamps.
*
* @param[in] pCurrentTime The more recent timestamp.
* @param[in] pOlderTime The older timestamp.
*
* @note This functions supports the edge case of SNTP timestamp overflow
* when @p pCurrentTime represents time in NTP era 1 (i.e. time since 7 Feb 2036)
* and the @p OlderTime represents time in NTP era 0 (i.e. time since 1st Jan 1900).
*
* @return Returns the calculated time duration between the two timestamps.
*
* @note This function returns the calculated time difference as unsigned 64 bit
* to avoid integer overflow when converting time difference between the seconds part
* of the timestamps, which are 32 bits wide, to milliseconds.
*/
static uint64_t calculateElapsedTimeMs( const SntpTimestamp_t * pCurrentTime,
const SntpTimestamp_t * pOlderTime )
{
uint64_t timeDiffMs = 0UL;
uint32_t timeDiffSec = 0U;
assert( pCurrentTime != NULL );
assert( pOlderTime != NULL );
/* Detect if SNTP time has overflown between the 2 timestamps. */
if( pCurrentTime->seconds < pOlderTime->seconds )
{
/* Handle the SNTP time overflow by calculating the actual time
* duration from pOlderTime, that exists in NTP era 0, to pCurrentTime,
* that exists in NTP era 1. */
timeDiffSec = ( UINT32_MAX - pOlderTime->seconds ) + /* Time in NTP era 0. */
1U + /* Epoch time in NTP era 1, i.e. 7 Feb 2036 6h:14m:28s. */
pCurrentTime->seconds; /* Time in NTP era 1. */
timeDiffMs = ( uint64_t ) timeDiffSec * 1000UL;
}
else
{
timeDiffSec = ( pCurrentTime->seconds - pOlderTime->seconds );
timeDiffMs = ( uint64_t ) timeDiffSec * 1000UL;
}
if( pCurrentTime->fractions > pOlderTime->fractions )
{
timeDiffMs += ( ( uint64_t ) pCurrentTime->fractions - ( uint64_t ) pOlderTime->fractions ) /
( SNTP_FRACTION_VALUE_PER_MICROSECOND * 1000UL );
}
else
{
timeDiffMs -= ( ( uint64_t ) pOlderTime->fractions - ( uint64_t ) pCurrentTime->fractions ) /
( SNTP_FRACTION_VALUE_PER_MICROSECOND * 1000UL );
}
return timeDiffMs;
}
/**
* @brief Validates the content of the SNTP context passed to the APIs to
* check whether it represents an initialized context.
*
* @param[in] pContext The SNTP context to validate.
*
* @return Returns one of the following:
* - #SntpSuccess if the context is verified to be initialized.
* - #SntpErrorBadParameter if the context is NULL.
* - #SntpErrorContextNotInitialized if the context is validated to be initialized.
*/
static SntpStatus_t validateContext( const SntpContext_t * pContext )
{
SntpStatus_t status = SntpSuccess;
/* Check if the context parameter is invalid. */
if( pContext == NULL )
{
status = SntpErrorBadParameter;
LogError( ( "Invalid context parameter: Context is NULL" ) );
}
/* Validate pointer parameters are not NULL. */
else if( ( pContext->pTimeServers == NULL ) || ( pContext->pNetworkBuffer == NULL ) ||
( pContext->resolveDnsFunc == NULL ) ||
( pContext->getTimeFunc == NULL ) || ( pContext->setTimeFunc == NULL ) )
{
status = SntpErrorContextNotInitialized;
}
/* Validate the size of the configured servers list, network buffer size and the state
* variable for the SNTP packet size.*/
else if( ( pContext->numOfServers == 0U ) || ( pContext->bufferSize < SNTP_PACKET_BASE_SIZE ) ||
( pContext->sntpPacketSize < SNTP_PACKET_BASE_SIZE ) )
{
status = SntpErrorContextNotInitialized;
}
/* Validate that the UDP transport interface functions are valid. */
else if( ( pContext->networkIntf.recvFrom == NULL ) || ( pContext->networkIntf.sendTo == NULL ) )
{
status = SntpErrorContextNotInitialized;
}
/* If an authentication interface is provided, validate that both its function pointer
* members are valid. */
else if( ( ( pContext->authIntf.generateClientAuth != NULL ) && ( pContext->authIntf.validateServerAuth == NULL ) ) ||
( ( pContext->authIntf.generateClientAuth == NULL ) && ( pContext->authIntf.validateServerAuth != NULL ) ) )
{
status = SntpErrorContextNotInitialized;
}
else
{
status = SntpSuccess;
}
if( status == SntpErrorContextNotInitialized )
{
LogError( ( "Invalid context parameter: Context is not initialized with Sntp_Init" ) );
}
return status;
}
/**
* @brief Sends SNTP request packet to the passed server over the network
* using transport interface's send function.
*
* @note For the case of zero byte transmissions over the network, this function
* repeatedly retries the send operation by calling the transport interface
* until either:
* 1. The requested number of bytes @p packetSize have been sent.
* OR
* 2. There is an error in sending data over the network.
*
* @note This function treats partial data transmissions as error as UDP
* transport protocol does not support partial sends.
*
* @param[in] pNetworkIntf The UDP transport interface to use for
* sending data over the network.
* @param[in] timeServer The IPv4 address of the server to send the
* SNTP request packet to.
* @param[in] serverPort The port of the @p timeServer to send the
* request to.
* @param[in] getTimeFunc The function to query system time for
* tracking retry time period of no data transmissions.
* @param[in] pPacket The buffer containing the SNTP packet data
* to send over the network.
* @param[in] packetSize The size of data in the SNTP request packet.
* @param[in] timeoutMs The timeout period for retry attempts of sending
* SNTP request packet over the network.
*
* @return Returns #SntpSuccess on successful transmission of the entire
* SNTP request packet over the network; #SntpErrorNetworkFailure
* to indicate failure from transport interface; #SntpErrorSendTimeout if
* time request could not be sent over the network within the @p timeoutMs
* duration.
*/
static SntpStatus_t sendSntpPacket( const UdpTransportInterface_t * pNetworkIntf,
uint32_t timeServer,
uint16_t serverPort,
SntpGetTime_t getTimeFunc,
const uint8_t * pPacket,
uint16_t packetSize,
uint32_t timeoutMs )
{
const uint8_t * pIndex = pPacket;
int32_t bytesSent = 0;
SntpTimestamp_t lastSendTime;
bool shouldRetry = false;
SntpStatus_t status = SntpErrorSendTimeout;
assert( pPacket != NULL );
assert( getTimeFunc != NULL );
assert( pNetworkIntf != NULL );
assert( packetSize >= SNTP_PACKET_BASE_SIZE );
/* Record the starting time of attempting to send data. This begins the retry timeout
* window. */
getTimeFunc( &lastSendTime );
/* Loop until the entire packet is sent. */
do
{
/* Reset flag for retrying send operation for the iteration. If request packet cannot be
* sent and timeout has not occurred, the flag will be set later for the next iteration. */
shouldRetry = false;
bytesSent = pNetworkIntf->sendTo( pNetworkIntf->pUserContext,
timeServer,
serverPort,
pIndex,
packetSize );
if( bytesSent < 0 )
{
LogError( ( "Unable to send request packet: Transport send failed. "
"ErrorCode=%ld.", ( long int ) bytesSent ) );
status = SntpErrorNetworkFailure;
}
else if( bytesSent == 0 )
{
/* No bytes were sent over the network. Retry send if we have not timed out. */
SntpTimestamp_t currentTime;
uint64_t elapsedTimeMs;
getTimeFunc( &currentTime );
/* Calculate time elapsed since last data was sent over network. */
elapsedTimeMs = calculateElapsedTimeMs( &currentTime, &lastSendTime );
/* Check for timeout if we have been waiting to send any data over the network. */
if( elapsedTimeMs >= timeoutMs )
{
LogError( ( "Unable to send request packet: Timed out retrying send: "
"SendRetryTimeout=%ums", timeoutMs ) );
status = SntpErrorSendTimeout;
}
else
{
shouldRetry = true;
}
}
/* Partial sends are not supported by UDP, which only supports sending the entire datagram as a whole.
* Thus, if the transport send function returns status representing partial send, it will be treated as failure. */
else if( bytesSent != ( int32_t ) packetSize )
{
LogError( ( "Unable to send request packet: Transport send returned unexpected bytes sent. "
"ReturnCode=%ld, ExpectedCode=%u", ( long int ) bytesSent, packetSize ) );
status = SntpErrorNetworkFailure;
}
else
{
/* The time request packet has been sent over the network. */
status = SntpSuccess;
}
} while( shouldRetry == true );
return status;
}
/**
* @brief Adds client authentication data to SNTP request packet by calling the
* authentication interface.
*
* @param[in] pContext The SNTP context.
*
* @return Returns one of the following:
* - #SntpSuccess if the interface function successfully appends client
* authentication data.
* - #SntpErrorAuthFailure when the interface returns either an error OR an
* incorrect size of the client authentication data.
*/
static SntpStatus_t addClientAuthentication( SntpContext_t * pContext )
{
SntpStatus_t status = SntpSuccess;
uint16_t authDataSize = 0U;
assert( pContext != NULL );
assert( pContext->authIntf.generateClientAuth != NULL );
assert( pContext->currentServerIndex <= pContext->numOfServers );
status = pContext->authIntf.generateClientAuth( pContext->authIntf.pAuthContext,
&pContext->pTimeServers[ pContext->currentServerIndex ],
pContext->pNetworkBuffer,
pContext->bufferSize,
&authDataSize );
if( status != SntpSuccess )
{
LogError( ( "Unable to send time request: Client authentication function failed: "
"RetStatus=%s", Sntp_StatusToStr( status ) ) );
}
/* Sanity check that the returned authentication data size fits in the remaining space
* of the request buffer besides the first #SNTP_PACKET_BASE_SIZE bytes. */
else if( authDataSize > ( pContext->bufferSize - SNTP_PACKET_BASE_SIZE ) )
{
LogError( ( "Unable to send time request: Invalid authentication code size: "
"AuthCodeSize=%lu, NetworkBufferSize=%lu",
( unsigned long ) authDataSize, ( unsigned long ) pContext->bufferSize ) );
status = SntpErrorAuthFailure;
}
else
{
/* With the authentication data added. calculate total SNTP request packet size. The same
* size would be expected in the SNTP response from server. */
pContext->sntpPacketSize = SNTP_PACKET_BASE_SIZE + authDataSize;
LogInfo( ( "Appended client authentication code to SNTP request packet:"
" AuthCodeSize=%lu, TotalPacketSize=%lu",
( unsigned long ) authDataSize,
( unsigned long ) pContext->sntpPacketSize ) );
}
return status;
}
SntpStatus_t Sntp_SendTimeRequest( SntpContext_t * pContext,
uint32_t randomNumber,
uint32_t blockTimeMs )
{
SntpStatus_t status = SntpSuccess;
/* Validate the context parameter. */
status = validateContext( pContext );
if( status == SntpSuccess )
{
const SntpServerInfo_t * pServer = NULL;
/* Set local variable for the currently indexed server to use for time
* query. */
pServer = &pContext->pTimeServers[ pContext->currentServerIndex ];
LogDebug( ( "Using server %.*s for time query", ( int ) pServer->serverNameLen, pServer->pServerName ) );
/* Perform DNS resolution of the currently indexed server in the list
* of configured servers. */
if( pContext->resolveDnsFunc( pServer, &pContext->currentServerAddr ) == false )
{
LogError( ( "Unable to send time request: DNS resolution failed: Server=%.*s",
( int ) pServer->serverNameLen, pServer->pServerName ) );
status = SntpErrorDnsFailure;
}
else
{
LogDebug( ( "Server DNS resolved: Address=0x%08X", pContext->currentServerAddr ) );
}
if( status == SntpSuccess )
{
/* Obtain current system time to generate SNTP request packet. */
pContext->getTimeFunc( &pContext->lastRequestTime );
LogDebug( ( "Obtained current time for SNTP request packet: Time=%us %ums",
pContext->lastRequestTime.seconds, FRACTIONS_TO_MS( pContext->lastRequestTime.fractions ) ) );
/* Generate SNTP request packet with the current system time and
* the passed random number. */
status = Sntp_SerializeRequest( &pContext->lastRequestTime,
randomNumber,
pContext->pNetworkBuffer,
pContext->bufferSize );
/* The serialization should be successful as all parameter validation has
* been done before. */
assert( status == SntpSuccess );
}
/* If an authentication interface has been configured, call the function to append client
* authentication data to SNTP request buffer. */
if( ( status == SntpSuccess ) && ( pContext->authIntf.generateClientAuth != NULL ) )
{
status = addClientAuthentication( pContext );
}
if( status == SntpSuccess )
{
LogInfo( ( "Sending serialized SNTP request packet to the server: Addr=%u, Port=%u",
pContext->currentServerAddr,
pContext->pTimeServers[ pContext->currentServerIndex ].port ) );
/* Send the request packet over the network to the time server. */
status = sendSntpPacket( &pContext->networkIntf,
pContext->currentServerAddr,
pContext->pTimeServers[ pContext->currentServerIndex ].port,
pContext->getTimeFunc,
pContext->pNetworkBuffer,
pContext->sntpPacketSize,
blockTimeMs );
}
}
return status;
}
/**
* @brief Utility to update the SNTP context to rotate the server of use for subsequent
* time request(s).
*
* @note If there is no next server remaining, after the current server's index, in the list of
* configured servers, the server rotation algorithm wraps around to the first server in the list.
* The wrap around is done so that an application using the library for a long-running SNTP client
* functionality (like a daemon task) does not become dysfunctional after all configured time
* servers have been used up. Time synchronization can be a critical functionality for a system
* and the wrap around logic ensures that the SNTP client continues to function in such a case.
*
* @note Server rotation is performed ONLY when either of:
* - The current server responds with a rejection for time request.
* OR
* - The current server response wait has timed out.
*/
static void rotateServerForNextTimeQuery( SntpContext_t * pContext )
{
size_t nextServerIndex = ( pContext->currentServerIndex + 1U ) % pContext->numOfServers;
LogInfo( ( "Rotating server for next time query: PreviousServer=%.*s, NextServer=%.*s",
( int ) pContext->pTimeServers[ pContext->currentServerIndex ].serverNameLen,
pContext->pTimeServers[ pContext->currentServerIndex ].pServerName,
( int ) pContext->pTimeServers[ nextServerIndex ].serverNameLen,
pContext->pTimeServers[ nextServerIndex ].pServerName ) );
pContext->currentServerIndex = nextServerIndex;
}
/**
* @brief This function attempts to receive the SNTP response packet from a server.
*
* @note This function treats reads of data sizes less than the expected server response packet,
* as an error as UDP does not support partial reads. Such a scenario can exist either due:
* - An error in the server sending its response with smaller packet size than the request packet OR
* - A malicious attacker spoofing or modifying server response OR
* - An error in the UDP transport interface implementation for read operation.
*
* @param[in] pTransportIntf The UDP transport interface to use for receiving data from
* the network.
* @param[in] timeServer The server to read the response from the network.
* @param[in] serverPort The port of the server to read the response from.
* @param[in, out] pBuffer This will be filled with the server response read from the
* network.
* @param[in] responseSize The size of server response to read from the network.
*
* @return It returns one of the following:
* - #SntpSuccess if an SNTP response packet is received from the network.
* - #SntpNoResponseReceived if a server response is not received from the network.
* - #SntpErrorNetworkFailure if there is an internal failure in reading from the network
* in the user-defined transport interface.
*/
static SntpStatus_t receiveSntpResponse( const UdpTransportInterface_t * pTransportIntf,
uint32_t timeServer,
uint16_t serverPort,
uint8_t * pBuffer,
uint16_t responseSize )
{
SntpStatus_t status = SntpNoResponseReceived;
int32_t bytesRead = 0;
assert( pTransportIntf != NULL );
assert( pTransportIntf->recvFrom != NULL );
assert( pBuffer != NULL );
assert( responseSize >= SNTP_PACKET_BASE_SIZE );
bytesRead = pTransportIntf->recvFrom( pTransportIntf->pUserContext,
timeServer,
serverPort,
pBuffer,
responseSize );
/* Negative return code indicates error. */
if( bytesRead < 0 )
{
status = SntpErrorNetworkFailure;
LogError( ( "Unable to receive server response: Transport receive failed: Code=%ld",
( long int ) bytesRead ) );
}
/* If the packet was not available on the network, check whether we can retry. */
else if( bytesRead == 0 )
{
status = SntpNoResponseReceived;
}
/* Partial reads are not supported by UDP, which only supports receiving the entire datagram as a whole.
* Thus, if the transport receive function returns reception of partial data, it will be treated as failure. */
else if( bytesRead != ( int32_t ) responseSize )
{
LogError( ( "Failed to receive server response: Transport recv returned less than expected bytes."
"ExpectedBytes=%u, ReadBytes=%ld", responseSize, ( long int ) bytesRead ) );
status = SntpErrorNetworkFailure;
}
else
{
LogDebug( ( "Received server response: PacketSize=%ld", ( long int ) bytesRead ) );
status = SntpSuccess;
}
return status;
}
/**
* @brief Processes the response from a server by de-serializing the SNTP packet to
* validate the server (if an authentication interface has been configured), determine
* whether server has accepted or rejected the time request, and update the system clock
* if the server responded positively with time.
*
* @param[in] pContext The SNTP context representing the SNTP client.
* @param[in] pResponseRxTime The time of receiving the server response from the network.
*
* @return It returns one of the following:
* - #SntpSuccess if the server response is successfully de-serialized and system clock
* updated.
* - #SntpErrorAuthFailure if there is internal failure in user-defined authentication
* interface when validating server from the response.
* - #SntpServerNotAuthenticated if the server failed authenticated check in the user-defined
* interface.
* - #SntpRejectedResponse if the server has rejected the time request in its response.
* - #SntpInvalidResponse if the server response failed sanity checks.
*/
static SntpStatus_t processServerResponse( SntpContext_t * pContext,
const SntpTimestamp_t * pResponseRxTime )
{
SntpStatus_t status = SntpSuccess;
const SntpServerInfo_t * pServer = &pContext->pTimeServers[ pContext->currentServerIndex ];
assert( pContext != NULL );
assert( pResponseRxTime != NULL );
if( pContext->authIntf.validateServerAuth != NULL )
{
/* Verify the server from the authentication data in the SNTP response packet. */
status = pContext->authIntf.validateServerAuth( pContext->authIntf.pAuthContext,
pServer,
pContext->pNetworkBuffer,
pContext->sntpPacketSize );
assert( ( status == SntpSuccess ) || ( status == SntpErrorAuthFailure ) ||
( status == SntpServerNotAuthenticated ) );
if( status != SntpSuccess )
{
LogError( ( "Unable to use server response: Server authentication function failed: "
"ReturnStatus=%s", Sntp_StatusToStr( status ) ) );
}
else
{
LogDebug( ( "Server response has been validated: Server=%.*s", ( int ) pServer->serverNameLen, pServer->pServerName ) );
}
}
if( status == SntpSuccess )
{
SntpResponseData_t parsedResponse;
/* De-serialize response packet to determine whether the server accepted or rejected
* the request for time. Also, calculate the system clock offset if the server responded
* with time. */
status = Sntp_DeserializeResponse( &pContext->lastRequestTime,
pResponseRxTime,
pContext->pNetworkBuffer,
pContext->sntpPacketSize,
&parsedResponse );
/* We do not expect the following errors to be returned as the context
* has been validated in the Sntp_ReceiveTimeResponse API. */
assert( status != SntpErrorBadParameter );
assert( status != SntpErrorBufferTooSmall );
if( ( status == SntpRejectedResponseChangeServer ) ||
( status == SntpRejectedResponseRetryWithBackoff ) ||
( status == SntpRejectedResponseOtherCode ) )
{
/* Server has rejected the time request. Thus, we will rotate to the next time server
* in the list. */
rotateServerForNextTimeQuery( pContext );
LogError( ( "Unable to use server response: Server has rejected request for time: RejectionCode=%.*s",
( int ) SNTP_KISS_OF_DEATH_CODE_LENGTH, ( char * ) &parsedResponse.rejectedResponseCode ) );
status = SntpRejectedResponse;
}
else if( status == SntpInvalidResponse )
{
LogError( ( "Unable to use server response: Server response failed sanity checks." ) );
}
else
{
/* Server has responded successfully with time, and we have calculated the clock offset
* of system clock relative to the server.*/
LogDebug( ( "Updating system time: ServerTime=%u %ums ClockOffset=%lums",
parsedResponse.serverTime.seconds, FRACTIONS_TO_MS( parsedResponse.serverTime.fractions ),
parsedResponse.clockOffsetMs ) );
/* Update the system clock with the calculated offset. */
pContext->setTimeFunc( pServer, &parsedResponse.serverTime,
parsedResponse.clockOffsetMs, parsedResponse.leapSecondType );
status = SntpSuccess;
}
}
/* Reset the last request time state in context to protect against replay attacks.
* Note: The last request time is not cleared when a rejection response packet is received and the client does
* has not authenticated server from the response. This is because clearing of the state causes the coreSNTP
* library to discard any subsequent server response packets (as the "originate timestamp" of those packets will
* not match the last request time value of the context), and thus, an attacker can cause Denial of Service
* attacks by spoofing server response before the actual server is able to respond.
*/
if( ( status == SntpSuccess ) ||
( ( pContext->authIntf.validateServerAuth != NULL ) && ( status == SntpRejectedResponse ) ) )
{
/* In the attack of SNTP request packet being replayed, the replayed request packet is serviced by
* SNTP/NTP server with SNTP response (as servers are stateless) and client receives the response
* containing new values of server timestamps but the stale value of "originate timestamp".
* To prevent the coreSNTP library from servicing such a server response (associated with the replayed
* SNTP request packet), the last request timestamp state is cleared in the context after receiving the
* first valid server response. Therefore, any subsequent server response(s) from replayed client request
* packets can be invalidated due to the "originate timestamp" not matching the last request time stored
* in the context.
* Note: If an attacker spoofs a server response with a zero "originate timestamp" after the coreSNTP
* library (i.e. the SNTP client) has cleared the internal state to zero, the spoofed packet will be
* discarded as the coreSNTP serializer does not accept server responses with zero value for timestamps.
*/
pContext->lastRequestTime.seconds = 0U;
pContext->lastRequestTime.fractions = 0U;
}
return status;
}
/**
* @brief Determines whether a retry attempt should be made to receive server response packet from the network
* depending on the timing constraints of server response timeout, @p responseTimeoutMs, and the block time
* period, @p blockTimeMs, passed. If neither of the time windows have expired, the function determines that the
* read operation can be re-tried.
*
* @param[in] pCurrentTime The current time in the system used for calculating elapsed time windows.
* @param[in] pReadStartTime The time of the first read attempt in the current set of read tries occurring
* from the Sntp_ReceiveTimeRequest API call by the application. This time is used for calculating the elapsed
* time to determine whether the block time has expired.
* @param[in] pRequestTime The time of sending the SNTP request to the server for which the response is
* awaited. This time is used for calculating total elapsed elapsed time of waiting for server response to
* determine if a server response timeout has occurred.
* @param[in] responseTimeoutMs The server response timeout configuration.
* @param[in] blockTimeMs The maximum block time of waiting for server response across read tries in the current
* call made by application to Sntp_ReceiveTimeResponse API.
* @param[out] pHasResponseTimedOut This will be populated with state to indicate whether the wait for server
* response has timed out.
*
* @return Returns true for retrying read operation of server response; false on either server response timeout
* OR completion of block time window.
*/
static bool decideAboutReadRetry( const SntpTimestamp_t * pCurrentTime,
const SntpTimestamp_t * pReadStartTime,
const SntpTimestamp_t * pRequestTime,
uint32_t responseTimeoutMs,
uint32_t blockTimeMs,
bool * pHasResponseTimedOut )
{
uint64_t timeSinceRequestMs = 0UL;
uint64_t timeElapsedInReadAttempts = 0UL;
bool shouldRetry = false;
assert( pCurrentTime != NULL );
assert( pReadStartTime != NULL );
assert( pRequestTime != NULL );
assert( pHasResponseTimedOut != NULL );
/* Calculate time elapsed since the time request was sent to the server
* to determine whether the server response has timed out. */
timeSinceRequestMs = calculateElapsedTimeMs( pCurrentTime, pRequestTime );
/* Calculate the time elapsed across all the read attempts so far to determine
* whether the block time window for reading server response has expired. */
timeElapsedInReadAttempts = calculateElapsedTimeMs( pCurrentTime, pReadStartTime );
/* Check whether a response timeout has occurred to inform whether we should
* wait for server response anymore. */
if( timeSinceRequestMs >= ( uint64_t ) responseTimeoutMs )
{
shouldRetry = false;
*pHasResponseTimedOut = true;
LogError( ( "Unable to receive response: Server response has timed out: "
"RequestTime=%us %ums, TimeoutDuration=%ums, ElapsedTime=%lu",
pRequestTime->seconds, FRACTIONS_TO_MS( pRequestTime->fractions ),
responseTimeoutMs, timeSinceRequestMs ) );
}
/* Check whether the block time window has expired to determine whether read can be retried. */
else if( timeElapsedInReadAttempts >= ( uint64_t ) blockTimeMs )
{
shouldRetry = false;
LogDebug( ( "Did not receive server response: Read block time has expired: "
"BlockTime=%ums, ResponseWaitElapsedTime=%lums",
blockTimeMs, timeSinceRequestMs ) );
}
else
{
shouldRetry = true;
LogDebug( ( "Did not receive server response: Retrying read: "
"BlockTime=%ums, ResponseWaitElapsedTime=%lums, ResponseTimeout=%u",
blockTimeMs, timeSinceRequestMs, responseTimeoutMs ) );
}
return shouldRetry;
}
SntpStatus_t Sntp_ReceiveTimeResponse( SntpContext_t * pContext,
uint32_t blockTimeMs )
{
SntpStatus_t status = SntpNoResponseReceived;
bool hasResponseTimedOut = false;
/* Validate the context parameter. */
status = validateContext( pContext );
if( status == SntpSuccess )
{
SntpTimestamp_t startTime, currentTime;
const SntpTimestamp_t * pRequestTime = &pContext->lastRequestTime;
bool shouldRetry = false;
/* Record time before read attempts so that it can be used as base time for
* for tracking the block time window across read retries. */
pContext->getTimeFunc( &startTime );
do
{
/* Reset the retry read operation flag. If the server response is not received in the current iteration's read
* attempt and the wait has not timed out, the flag will be set to perform a retry. */
shouldRetry = false;
/* Make an attempt to read the server response from the network. */
status = receiveSntpResponse( &pContext->networkIntf,
pContext->currentServerAddr,
pContext->pTimeServers[ pContext->currentServerIndex ].port,
pContext->pNetworkBuffer,
pContext->sntpPacketSize );
/* If the server response is received, deserialize it, validate the server
* (if authentication interface is provided), and update system time with
* the calculated clock offset. */
if( status == SntpSuccess )
{
/* Get current time to de-serialize the receive server response packet. */
pContext->getTimeFunc( &currentTime );
status = processServerResponse( pContext, &currentTime );
}
else if( status == SntpNoResponseReceived )
{
/* Get current time to determine whether another attempt for reading the packet can
* be made. */
pContext->getTimeFunc( &currentTime );
/* Set the flag to retry read of server response from the network. */
shouldRetry = decideAboutReadRetry( &currentTime,
&startTime,
pRequestTime,
pContext->responseTimeoutMs,
blockTimeMs,
&hasResponseTimedOut );
}
else
{
/* Empty else marker. */
}
} while( shouldRetry == true );
/* If the wait for server response to the time request has timed out, rotate the server of use in the
* context for subsequent time request(s). Also, update the return status to indicate response timeout. */
if( hasResponseTimedOut == true )
{
status = SntpErrorResponseTimeout;
/* Rotate server to the next in the list of configured servers in the context. */
rotateServerForNextTimeQuery( pContext );
}
}
return status;
}
const char * Sntp_StatusToStr( SntpStatus_t status )
{
const char * pString = NULL;
switch( status )
{
case SntpSuccess:
pString = "SntpSuccess";
break;
case SntpErrorBadParameter:
pString = "SntpErrorBadParameter";
break;
case SntpRejectedResponseChangeServer:
pString = "SntpRejectedResponseChangeServer";
break;
case SntpRejectedResponseRetryWithBackoff:
pString = "SntpRejectedResponseRetryWithBackoff";
break;
case SntpRejectedResponseOtherCode:
pString = "SntpRejectedResponseOtherCode";
break;
case SntpErrorBufferTooSmall:
pString = "SntpErrorBufferTooSmall";
break;
case SntpInvalidResponse:
pString = "SntpInvalidResponse";
break;
case SntpZeroPollInterval:
pString = "SntpZeroPollInterval";
break;
case SntpErrorTimeNotSupported:
pString = "SntpErrorTimeNotSupported";
break;
case SntpErrorDnsFailure:
pString = "SntpErrorDnsFailure";
break;
case SntpErrorNetworkFailure:
pString = "SntpErrorNetworkFailure";
break;
case SntpServerNotAuthenticated:
pString = "SntpServerNotAuthenticated";
break;
case SntpErrorAuthFailure:
pString = "SntpErrorAuthFailure";
break;
default:
pString = "Invalid status code!";
break;
}
return pString;
}

View File

@ -0,0 +1,853 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_serializer.c
* @brief Implementation of the Serializer API of the coreSNTP library.
*/
/* Standard includes. */
#include <string.h>
#include <stdbool.h>
#include <assert.h>
/* Include API header. */
#include "core_sntp_serializer.h"
#include "core_sntp_config_defaults.h"
/**
* @brief The version of SNTP supported by the coreSNTP library by complying
* with the SNTPv4 specification defined in [RFC 4330](https://tools.ietf.org/html/rfc4330).
*/
#define SNTP_VERSION ( 4U )
/**
* @brief The bit mask for the "Mode" information in the first byte of an SNTP packet.
* The "Mode" field occupies bits 0-2 of the byte.
* @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
* for more information.
*/
#define SNTP_MODE_BITS_MASK ( 0x07U )
/**
* @brief The value indicating a "client" in the "Mode" field of an SNTP packet.
* @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
* for more information.
*/
#define SNTP_MODE_CLIENT ( 3U )
/**
* @brief The value indicating a "server" in the "Mode" field of an SNTP packet.
* @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
* for more information.
*/
#define SNTP_MODE_SERVER ( 4U )
/**
* @brief The position of the least significant bit of the "Leap Indicator" field
* in first byte of an SNTP packet. The "Leap Indicator" field occupies bits 6-7 of the byte.
* @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
* for more information.
*/
#define SNTP_LEAP_INDICATOR_LSB_POSITION ( 6 )
/**
* @brief Value of Stratum field in SNTP packet representing a Kiss-o'-Death message
* from server.
*/
#define SNTP_KISS_OF_DEATH_STRATUM ( 0U )
/**
* @brief The position of least significant bit of the "Version" information
* in the first byte of an SNTP packet. "Version" field occupies bits 3-5 of
* the byte.
* @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
* for more information.
*/
#define SNTP_VERSION_LSB_POSITION ( 3 )
/**
* @brief The integer value of the Kiss-o'-Death ASCII code, "DENY", used
* for comparison with data in an SNTP response.
* @note Refer to [RFC 4330 Section 8](https://tools.ietf.org/html/rfc4330#section-8)
* for more information.
*/
#define KOD_CODE_DENY_UINT_VALUE ( 0x44454e59U )
/**
* @brief The integer value of the Kiss-o'-Death ASCII code, "RSTR", used
* for comparison with data in an SNTP response.
* @note Refer to [RFC 4330 Section 8](https://tools.ietf.org/html/rfc4330#section-8)
* for more information.
*/
#define KOD_CODE_RSTR_UINT_VALUE ( 0x52535452U )
/**
* @brief The integer value of the Kiss-o'-Death ASCII code, "RATE", used
* for comparison with data in an SNTP response.
* @note Refer to [RFC 4330 Section 8](https://tools.ietf.org/html/rfc4330#section-8)
* for more information.
*/
#define KOD_CODE_RATE_UINT_VALUE ( 0x52415445U )
/**
* @brief Macro to represent exactly half of the total number of seconds in an NTP era.
* As 32 bits are used to represent the "seconds" part of an SNTP timestamp, the half of
* the total range of seconds in an NTP era is 2^31 seconds, which represents ~68 years.
*
* @note This macro represents the edge case of calculating the client system clock-offset
* relative to server time as the library ASSUMES that the client and server times are within
* 2^31 seconds apart in duration. This is done to support calculating clock-offset for the
* cases when server and client systems are in adjacent NTP eras, which can occur when NTP time
* wraps around in 2036, and the relative NTP era presence of client and server times is
* determined based on comparing first order difference values between all possible NTP era
* configurations of the systems.
*/
#define CLOCK_OFFSET_MAX_TIME_DIFFERENCE ( ( ( ( int64_t ) INT32_MAX + 1 ) * 1000 ) )
/**
* @brief Macro to represent the total number of milliseconds that are represented in an
* NTP era period. This macro represents a duration of ~136 years.
*
* @note Rationale for calculation: The "seconds" part of an NTP timestamp is represented in
* unsigned 32 bit width, thus, the total range of seconds it represents is 2^32,
* i.e. (UINT32_MAX + 1).
*/
#define TOTAL_MILLISECONDS_IN_NTP_ERA ( ( ( int64_t ) UINT32_MAX + 1 ) * 1000 )
/**
* @brief Structure representing an SNTP packet header.
* For more information on SNTP packet format, refer to
* [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4).
*
* @note This does not include extension fields for authentication data
* for secure SNTP communication. Authentication data follows the
* packet header represented by this structure.
*/
typedef struct SntpPacket
{
/**
* @brief Bits 6-7 leap indicator, bits 3-5 are version number, bits 0-2 are mode
*/
uint8_t leapVersionMode;
/**
* @brief stratum
*/
uint8_t stratum;
/**
* @brief poll interval
*/
uint8_t poll;
/**
* @brief precision
*/
uint8_t precision;
/**
* @brief root delay
*/
uint32_t rootDelay;
/**
* @brief root dispersion
*/
uint32_t rootDispersion;
/**
* @brief reference ID
*/
uint32_t refId;
/**
* @brief reference time
*/
SntpTimestamp_t refTime;
/**
* @brief origin timestamp
*/
SntpTimestamp_t originTime;
/**
* @brief receive timestamp
*/
SntpTimestamp_t receiveTime;
/**
* @brief transmit timestamp
*/
SntpTimestamp_t transmitTime;
} SntpPacket_t;
/**
* @brief Utility macro to fill 32-bit integer in word-sized
* memory in network byte (or Big Endian) order.
*
* @param[out] pWordMemory Pointer to the word-sized memory in which
* the 32-bit integer will be filled.
* @param[in] data The 32-bit integer to fill in the @p wordMemory
* in network byte order.
*
* @note This utility ensures that data is filled in memory
* in expected network byte order, as an assignment operation
* (like *pWordMemory = word) can cause undesired side-effect
* of network-byte ordering getting reversed on Little Endian platforms.
*/
static void fillWordMemoryInNetworkOrder( uint32_t * pWordMemory,
uint32_t data )
{
assert( pWordMemory != NULL );
*( ( uint8_t * ) pWordMemory ) = ( uint8_t ) ( data >> 24 );
*( ( uint8_t * ) pWordMemory + 1 ) = ( uint8_t ) ( ( data >> 16 ) & 0x000000FFU );
*( ( uint8_t * ) pWordMemory + 2 ) = ( uint8_t ) ( ( data >> 8 ) & 0x000000FFU );
*( ( uint8_t * ) pWordMemory + 3 ) = ( uint8_t ) ( ( data ) & 0x000000FFU );
}
/**
* @brief Utility macro to generate a 32-bit integer from memory containing
* integer in network (or Big Endian) byte order.
* @param[in] ptr Pointer to the memory containing 32-bit integer in network
* byte order.
*
* @return The host representation of the 32-bit integer in the passed word
* memory.
*/
static uint32_t readWordFromNetworkByteOrderMemory( const uint32_t * ptr )
{
const uint8_t * pMemStartByte = ( const uint8_t * ) ptr;
assert( ptr != NULL );
return ( uint32_t ) ( ( ( uint32_t ) *( pMemStartByte ) << 24 ) |
( 0x00FF0000U & ( ( uint32_t ) *( pMemStartByte + 1 ) << 16 ) ) |
( 0x0000FF00U & ( ( uint32_t ) *( pMemStartByte + 2 ) << 8 ) ) |
( ( uint32_t ) *( pMemStartByte + 3 ) ) );
}
/**
* @brief Utility to return absolute (or positively signed) value of an signed
* 64 bit integer.
*
* @param[in] value The integer to return the absolute value of.
*
* @return The absolute value of @p value.
*/
static int64_t absoluteOf( int64_t value )
{
return ( value >= 0 ) ? value : ( ( int64_t ) 0 - value );
}
/**
* @brief Utility to determine whether a timestamp represents a zero
* timestamp value.
*
* @note This utility is used to determine whether a timestamp value is
* invalid. According to the SNTPv4 specification, a zero timestamp value
* is considered invalid.
*
* @param[in] pTime The timestamp whose value is to be inspected for
* zero value.
*
* @return `true` if the timestamp is zero; otherwise `false`.
*/
static bool isZeroTimestamp( const SntpTimestamp_t * pTime )
{
bool isSecondsZero = ( pTime->seconds == 0U ) ? true : false;
bool isFractionsZero = ( pTime->fractions == 0U ) ? true : false;
return( isSecondsZero && isFractionsZero );
}
/**
* @brief Utility to convert the "fractions" part of an SNTP timestamp to milliseconds
* duration of time.
* @param[in] fractions The fractions value.
*
* @return The milliseconds equivalent of the @p fractions value.
*/
static uint32_t fractionsToMs( uint32_t fractions )
{
return( fractions / ( 1000U * SNTP_FRACTION_VALUE_PER_MICROSECOND ) );
}
/**
* @brief Utility to safely calculate difference between server and client timestamps and
* return the difference in the resolution of milliseconds as a signed 64 bit integer.
* The calculated value represents the effective subtraction as ( @p serverTimeSec - @p clientTimeSec ).
*
* @note This utility SUPPORTS the cases of server and client timestamps being in different NTP eras,
* and ASSUMES that the server and client systems are within 68 years of each other.
* To handle the case of different NTP eras, this function calculates difference values for all
* possible combinations of NTP eras of server and client times (i.e. 1. both timestamps in same era,
* 2. server timestamp one era ahead, and 3. client timestamp being one era ahead), and determines
* the NTP era configuration by choosing the difference value of the smallest absolute value.
*
* @param[in] pServerTime The server timestamp.
* @param[in] pClientTime The client timestamp.
*
* @return The calculated difference between server and client times as a signed 64 bit integer.
*/
static int64_t safeTimeDifference( const SntpTimestamp_t * pServerTime,
const SntpTimestamp_t * pClientTime )
{
int64_t eraAdjustedDiff = 0;
/* Convert the timestamps into 64 bit signed integer values of milliseconds. */
int64_t serverTime = ( ( int64_t ) pServerTime->seconds * 1000 ) + ( int64_t ) fractionsToMs( pServerTime->fractions );
int64_t clientTime = ( ( int64_t ) pClientTime->seconds * 1000 ) + ( int64_t ) fractionsToMs( pClientTime->fractions );
/* The difference between the 2 timestamps is calculated by determining the whether the timestamps
* are present in the same NTP era or adjacent NTP eras (i.e. the NTP timestamp overflow case). */
/* First, calculate the first order time difference assuming that server and client times
* are in the same NTP era. */
int64_t diffWithNoEraAdjustment = serverTime - clientTime;
/* Store the absolute value of the time difference which will be used for comparison with
* different cases of relative NTP era configuration of client and server times. */
int64_t absSameEraDiff = absoluteOf( diffWithNoEraAdjustment );
/* If the absolute difference value is 2^31 seconds, it means that the server and client times are
* away by exactly half the range of SNTP timestamp "second" values representable in unsigned 32 bits.
* In such a case, irrespective of whether the 2 systems exist in the same or adjacent NTP eras, the
* time difference calculated between the systems will ALWAYS yield the same value when viewed from
* all NTP era configurations of the times.
* For such a case, we will ASSUME that the server time is AHEAD of client time, and thus, generate
* a positive clock-offset value.
*/
if( absSameEraDiff == CLOCK_OFFSET_MAX_TIME_DIFFERENCE )
{
/* It does not matter whether server and client are in the same era for this
* special case as the difference value for both same and adjacent eras will yield
* the same absolute value of 2^31.*/
eraAdjustedDiff = CLOCK_OFFSET_MAX_TIME_DIFFERENCE;
}
else
{
/* Determine if server time belongs to an NTP era different than the client time, and accordingly
* choose the 64 bit representation of the first order difference to account for the era.
* The logic for determining the relative era presence of the timestamps is by calculating the
* first order difference (of "Server Time - Client Time") for all the 3 different era combinations
* (1. both timestamps in same era, 2. server time one era ahead, 3. client time one era ahead)
* and choosing the NTP era configuration that has the smallest first order difference value.
*/
int64_t diffWithServerEraAdjustment = serverTime + TOTAL_MILLISECONDS_IN_NTP_ERA -
clientTime; /* This helps determine whether server is an
* era ahead of client time. */
int64_t diffWithClientEraAdjustment = serverTime -
( TOTAL_MILLISECONDS_IN_NTP_ERA + clientTime ); /* This helps determine whether server is an
* era behind of client time. */
/* Store the absolute value equivalents of all the time difference configurations
* for easier comparison to smallest value from them. */
int64_t absServerEraAheadDiff = absoluteOf( diffWithServerEraAdjustment );
int64_t absClientEraAheadDiff = absoluteOf( diffWithClientEraAdjustment );
/* Determine the correct relative era of client and server times by checking which era
* configuration of difference value represents the least difference. */
if( ( absSameEraDiff <= absServerEraAheadDiff ) && ( absSameEraDiff <= absClientEraAheadDiff ) )
{
/* Both server and client times are in the same era. */
eraAdjustedDiff = diffWithNoEraAdjustment;
}
/* Check if server time is an NTP era ahead of client time. */
else if( absServerEraAheadDiff < absSameEraDiff )
{
/* Server time is in NTP era 1 while client time is in NTP era 0. */
eraAdjustedDiff = diffWithServerEraAdjustment;
}
/* Now, we know that the client time is an era ahead of server time. */
else
{
/* Server time is in NTP era 0 while client time is in NTP era 1. */
eraAdjustedDiff = diffWithClientEraAdjustment;
}
}
return eraAdjustedDiff;
}
/**
* @brief Utility to calculate clock offset of system relative to the
* server using the on-wire protocol specified in the NTPv4 specification.
* For more information on on-wire protocol, refer to
* [RFC 5905 Section 8](https://tools.ietf.org/html/rfc5905#section-8).
*
* @note The following diagram explains the calculation of the clock
* offset:
*
* T2 T3
* --------------------------------- <----- *SNTP/NTP server*
* /\ \
* / \
* Request* / \ *Response*
* / \/
* --------------------------------- <----- *SNTP client*
* T1 T4
*
* The four most recent timestamps, T1 through T4, are used to compute
* the clock offset of SNTP client relative to the server where:
*
* T1 = Client Request Transmit Time
* T2 = Server Receive Time (of client request)
* T3 = Server Response Transmit Time
* T4 = Client Receive Time (of server response)
*
* Clock Offset = T(NTP/SNTP server) - T(SNTP client)
* = [( T2 - T1 ) + ( T3 - T4 )]
* ---------------------------
* 2
*
* @note Both NTPv4 and SNTPv4 specifications suggest calculating the
* clock offset value, if possible. As the timestamp format uses 64 bit
* integer and there exist 2 orders of arithmetic calculations on the
* timestamp values (subtraction followed by addition as shown in the
* diagram above), the clock offset for the system can be calculated
* ONLY if the value can be represented in 62 significant bits and 2 sign
* bits i.e. if the system clock is within 34 years (in the future or past)
* of the server time.
*
* @param[in] pClientTxTime The system time of sending the SNTP request.
* This is the same as "T1" in the above diagram.
* @param[in] pServerRxTime The server time of receiving the SNTP request
* packet from the client. This is the same as "T2" in the above diagram.
* @param[in] pServerTxTime The server time of sending the SNTP response
* packet. This is the same as "T3" in the above diagram.
* @param[in] pClientRxTime The system time of receiving the SNTP response
* from the server. This is the same as "T4" in the above diagram.
* @param[out] pClockOffset This will be filled with the calculated offset value
* of the system clock relative to the server time with the assumption that the
* system clock is within 68 years of server time.
*/
static void calculateClockOffset( const SntpTimestamp_t * pClientTxTime,
const SntpTimestamp_t * pServerRxTime,
const SntpTimestamp_t * pServerTxTime,
const SntpTimestamp_t * pClientRxTime,
int64_t * pClockOffset )
{
/* Variable for storing the first-order difference between timestamps. */
int64_t firstOrderDiffSend = 0;
int64_t firstOrderDiffRecv = 0;
assert( pClientTxTime != NULL );
assert( pServerRxTime != NULL );
assert( pServerTxTime != NULL );
assert( pClientRxTime != NULL );
assert( pClockOffset != NULL );
/* Perform first order difference of timestamps on the network send path i.e. T2 - T1.
* Note: The calculated difference value will always represent years in the range of
*[-68 years, +68 years]. */
firstOrderDiffSend = safeTimeDifference( pServerRxTime, pClientTxTime );
/* Perform first order difference of timestamps on the network receive path i.e. T3 - T4.
* Note: The calculated difference value will always represent years in the range of
*[-68 years, +68 years]. */
firstOrderDiffRecv = safeTimeDifference( pServerTxTime, pClientRxTime );
/* Now calculate the system clock-offset relative to server time as the average of the
* first order difference of timestamps in both directions of network path.
* Note: This will ALWAYS represent offset in the range of [-68 years, +68 years]. */
*pClockOffset = ( firstOrderDiffSend + firstOrderDiffRecv ) / 2;
}
/**
* @brief Parse a SNTP response packet by determining whether it is a rejected
* or accepted response to an SNTP request, and accordingly, populate the
* @p pParsedResponse parameter with the parsed data.
*
* @note If the server has rejected the request with the a Kiss-o'-Death message,
* then this function will set the associated rejection code in the output parameter
* while setting the remaining members to zero.
* If the server has accepted the time request, then the function will set the
* rejectedResponseCode member of the output parameter to #SNTP_KISS_OF_DEATH_CODE_NONE,
* and set the other the members with appropriate data extracted from the response
* packet.
*
* @param[in] pResponsePacket The SNTP response packet from server to parse.
* @param[in] pRequestTxTime The system time (in SNTP timestamp format) of
* sending the SNTP request to server.
* @param[in] pResponseRxTime The system time (in SNTP timestamp format) of
* receiving the SNTP response from server.
* @param[out] pParsedResponse The parameter that will be populated with data
* parsed from the response packet, @p pResponsePacket.
*
* @return This function returns one of the following:
* - #SntpSuccess if the server response does not represent a Kiss-o'-Death
* message.
* - #SntpRejectedResponseChangeServer if the server rejected with a code
* indicating that client cannot be retry requests to it.
* - #SntpRejectedResponseRetryWithBackoff if the server rejected with a code
* indicating that client should back-off before retrying request.
* - #SntpRejectedResponseOtherCode if the server rejected with a code
* other than "DENY", "RSTR" and "RATE".
*/
static SntpStatus_t parseValidSntpResponse( const SntpPacket_t * pResponsePacket,
const SntpTimestamp_t * pRequestTxTime,
const SntpTimestamp_t * pResponseRxTime,
SntpResponseData_t * pParsedResponse )
{
SntpStatus_t status = SntpSuccess;
assert( pResponsePacket != NULL );
assert( pResponseRxTime != NULL );
assert( pParsedResponse != NULL );
/* Clear the output parameter memory to zero. */
( void ) memset( pParsedResponse, 0, sizeof( *pParsedResponse ) );
/* Determine if the server has accepted or rejected the request for time. */
if( pResponsePacket->stratum == SNTP_KISS_OF_DEATH_STRATUM )
{
/* Server has sent a Kiss-o'-Death message i.e. rejected the request. */
/* Extract the kiss-code sent by the server from the "Reference ID" field
* of the SNTP packet. */
pParsedResponse->rejectedResponseCode =
readWordFromNetworkByteOrderMemory( &pResponsePacket->refId );
/* Determine the return code based on the Kiss-o'-Death code. */
switch( pParsedResponse->rejectedResponseCode )
{
case KOD_CODE_DENY_UINT_VALUE:
case KOD_CODE_RSTR_UINT_VALUE:
status = SntpRejectedResponseChangeServer;
break;
case KOD_CODE_RATE_UINT_VALUE:
status = SntpRejectedResponseRetryWithBackoff;
break;
default:
status = SntpRejectedResponseOtherCode;
break;
}
}
else
{
/* Server has responded successfully to the time request. */
SntpTimestamp_t serverRxTime;
/* Map of integer value to SntpLeapSecondInfo_t enumeration type for the
* "Leap Indicator" field in the first byte of an SNTP packet.
* Note: This map is used to not violate MISRA Rule 10.5 when directly
* converting an integer to enumeration type.
*/
const SntpLeapSecondInfo_t leapIndicatorTypeMap[] =
{
NoLeapSecond,
LastMinuteHas61Seconds,
LastMinuteHas59Seconds,
AlarmServerNotSynchronized
};
/* Set the Kiss-o'-Death code value to NULL as server has responded favorably
* to the time request. */
pParsedResponse->rejectedResponseCode = SNTP_KISS_OF_DEATH_CODE_NONE;
/* Fill the output parameter with the server time which is the
* "transmit" time in the response packet. */
pParsedResponse->serverTime.seconds =
readWordFromNetworkByteOrderMemory( &pResponsePacket->transmitTime.seconds );
pParsedResponse->serverTime.fractions =
readWordFromNetworkByteOrderMemory( &pResponsePacket->transmitTime.fractions );
/* Extract information of any upcoming leap second from the response. */
pParsedResponse->leapSecondType = leapIndicatorTypeMap[
( pResponsePacket->leapVersionMode >> SNTP_LEAP_INDICATOR_LSB_POSITION ) ];
/* Store the "receive" time in SNTP response packet in host order. */
serverRxTime.seconds =
readWordFromNetworkByteOrderMemory( &pResponsePacket->receiveTime.seconds );
serverRxTime.fractions =
readWordFromNetworkByteOrderMemory( &pResponsePacket->receiveTime.fractions );
/* Calculate system clock offset relative to server time, if possible, within
* the 64 bit integer width of the SNTP timestamp. */
calculateClockOffset( pRequestTxTime,
&serverRxTime,
&pParsedResponse->serverTime,
pResponseRxTime,
&pParsedResponse->clockOffsetMs );
}
return status;
}
SntpStatus_t Sntp_SerializeRequest( SntpTimestamp_t * pRequestTime,
uint32_t randomNumber,
void * pBuffer,
size_t bufferSize )
{
SntpStatus_t status = SntpSuccess;
if( pRequestTime == NULL )
{
status = SntpErrorBadParameter;
}
else if( pBuffer == NULL )
{
status = SntpErrorBadParameter;
}
else if( bufferSize < SNTP_PACKET_BASE_SIZE )
{
status = SntpErrorBufferTooSmall;
}
/* Zero timestamps for client request time is not allowed to protect against
* attack spoofing server response containing zero value for "originate timestamp".
* Note: In SNTP/NTP communication, the "originate timestamp" of a valid server response
* matches the "transmit timestamp" in corresponding client request packet. */
else if( isZeroTimestamp( pRequestTime ) == true )
{
status = SntpErrorBadParameter;
}
else
{
/* MISRA Ref 11.5.1 [Void pointer assignment] */
/* More details at: https://github.com/FreeRTOS/coreSNTP/blob/main/MISRA.md#rule-115 */
/* coverity[misra_c_2012_rule_11_5_violation] */
SntpPacket_t * pRequestPacket = ( SntpPacket_t * ) pBuffer;
/* Fill the buffer with zero as most fields are zero for a standard SNTP
* request packet.*/
( void ) memset( pBuffer, 0, sizeof( SntpPacket_t ) );
/* Set the first byte of the request packet for "Version" and "Mode" fields */
pRequestPacket->leapVersionMode = 0U /* Leap Indicator */ |
( SNTP_VERSION << SNTP_VERSION_LSB_POSITION ) /* Version Number */ |
SNTP_MODE_CLIENT /* Mode */;
/* Add passed random number to non-significant bits of the fractions part
* of the transmit timestamp.
* This is suggested by the SNTPv4 (and NTPv4) specification(s)
* to protect against replay attacks. Refer to RFC 4330 Section 3 for
* more information.
* Adding random bits to the least significant 16 bits of the fractions
* part of the timestamp affects only ~15 microseconds of information
* (calculated as 0xFFFF * 232 picoseconds).
*/
pRequestTime->fractions = ( pRequestTime->fractions
| ( randomNumber >> 16 ) );
/* Update the request buffer with request timestamp in network byte order. */
fillWordMemoryInNetworkOrder( &pRequestPacket->transmitTime.seconds,
pRequestTime->seconds );
fillWordMemoryInNetworkOrder( &pRequestPacket->transmitTime.fractions,
pRequestTime->fractions );
}
return status;
}
SntpStatus_t Sntp_DeserializeResponse( const SntpTimestamp_t * pRequestTime,
const SntpTimestamp_t * pResponseRxTime,
const void * pResponseBuffer,
size_t bufferSize,
SntpResponseData_t * pParsedResponse )
{
SntpStatus_t status = SntpSuccess;
/* MISRA Ref 11.5.1 [Void pointer assignment] */
/* More details at: https://github.com/FreeRTOS/coreSNTP/blob/main/MISRA.md#rule-115 */
/* coverity[misra_c_2012_rule_11_5_violation] */
const SntpPacket_t * pResponsePacket = ( const SntpPacket_t * ) pResponseBuffer;
if( ( pRequestTime == NULL ) || ( pResponseRxTime == NULL ) ||
( pResponseBuffer == NULL ) || ( pParsedResponse == NULL ) )
{
status = SntpErrorBadParameter;
}
else if( bufferSize < SNTP_PACKET_BASE_SIZE )
{
status = SntpErrorBufferTooSmall;
}
/* Zero timestamps for client request time is not allowed to protect against
* attack spoofing server response containing zero value for "originate timestamp".
* Note: In SNTP/NTP communication, the "originate timestamp" of a valid server response
* matches the "transmit timestamp" in corresponding client request packet. */
else if( isZeroTimestamp( pRequestTime ) == true )
{
status = SntpErrorBadParameter;
}
/* Check if the packet represents a server in the "Mode" field. */
else if( ( pResponsePacket->leapVersionMode & SNTP_MODE_BITS_MASK ) != SNTP_MODE_SERVER )
{
status = SntpInvalidResponse;
}
/* Check if any of the timestamps in the response packet are zero, which is invalid.
* Note: This is done to protect against a nuanced server spoofing attack where if the
* SNTP client resets its internal state of "Client transmit timestamp" (OR "originate
* timestamp" from server perspective) to zero as a protection against replay attack, an
* an attacker with this knowledge of the client operation can spoof a server response
* containing the "originate timestamp" as zero. Thus, to protect against such attack,
* a server response packet with any zero timestamp is rejected. */
else if( ( isZeroTimestamp( &pResponsePacket->originTime ) == true ) ||
( isZeroTimestamp( &pResponsePacket->receiveTime ) == true ) ||
( isZeroTimestamp( &pResponsePacket->transmitTime ) == true ) )
{
status = SntpInvalidResponse;
}
/* Validate that the server has sent the client's request timestamp in the
* "originate" timestamp field of the response. */
else if( ( pRequestTime->seconds !=
readWordFromNetworkByteOrderMemory( &pResponsePacket->originTime.seconds ) ) ||
( pRequestTime->fractions !=
readWordFromNetworkByteOrderMemory( &pResponsePacket->originTime.fractions ) ) )
{
status = SntpInvalidResponse;
}
else
{
/* As the response packet is valid, parse more information from it and
* populate the output parameter. */
status = parseValidSntpResponse( pResponsePacket,
pRequestTime,
pResponseRxTime,
pParsedResponse );
}
return status;
}
SntpStatus_t Sntp_CalculatePollInterval( uint16_t clockFreqTolerance,
uint16_t desiredAccuracy,
uint32_t * pPollInterval )
{
SntpStatus_t status = SntpSuccess;
if( ( clockFreqTolerance == 0U ) || ( desiredAccuracy == 0U ) || ( pPollInterval == NULL ) )
{
status = SntpErrorBadParameter;
}
else
{
uint32_t exactIntervalForAccuracy = 0U;
uint8_t log2PollInterval = 0U;
/* Calculate the poll interval required for achieving the exact desired clock accuracy
* with the following formulae:
*
* System Clock Drift Rate ( microseconds / second ) = Clock Frequency Tolerance (in PPM )
* Maximum Clock Drift ( milliseconds ) = Desired Accuracy ( milliseconds )
*
* Poll Interval ( seconds ) = Maximum Clock Drift
* ---------------------------
* System Clock Drift Rate
*
* = Maximum Drift ( milliseconds ) * 1000 ( microseconds / millisecond )
* ------------------------------------------------------------------------
* System Clock Drift Rate ( microseconds / second )
*
* = Desired Accuracy * 1000
* ------------------------------
* Clock Frequency Tolerance
*/
exactIntervalForAccuracy = ( ( uint32_t ) desiredAccuracy * 1000U ) / clockFreqTolerance;
/* Check if calculated poll interval value falls in the supported range of seconds. */
if( exactIntervalForAccuracy == 0U )
{
/* Poll interval value is less than 1 second, and is not supported by the function. */
status = SntpZeroPollInterval;
}
else
{
/* To calculate the log 2 value of the exact poll interval value, first determine the highest
* bit set in the value. */
while( exactIntervalForAccuracy != 0U )
{
log2PollInterval++;
exactIntervalForAccuracy /= 2U;
}
/* Convert the highest bit in the exact poll interval value to the nearest integer
* value lower or equal to the log2 of the exact poll interval value. */
log2PollInterval--;
/* Calculate the poll interval as the closest exponent of 2 value that achieves
* equal or higher accuracy than the desired accuracy. */
*pPollInterval = ( ( ( uint32_t ) 1U ) << log2PollInterval );
}
}
return status;
}
SntpStatus_t Sntp_ConvertToUnixTime( const SntpTimestamp_t * pSntpTime,
uint32_t * pUnixTimeSecs,
uint32_t * pUnixTimeMicrosecs )
{
SntpStatus_t status = SntpSuccess;
if( ( pSntpTime == NULL ) || ( pUnixTimeSecs == NULL ) || ( pUnixTimeMicrosecs == NULL ) )
{
status = SntpErrorBadParameter;
}
/* Check if passed time does not lie in the [UNIX epoch in 1970, UNIX time overflow in 2038] time range. */
else if( ( pSntpTime->seconds > SNTP_TIME_AT_LARGEST_UNIX_TIME_SECS ) &&
( pSntpTime->seconds < SNTP_TIME_AT_UNIX_EPOCH_SECS ) )
{
/* The SNTP timestamp is outside the supported time range for conversion. */
status = SntpErrorTimeNotSupported;
}
else
{
/* Handle case when timestamp represents date in SNTP era 1
* (i.e. time from 7 Feb 2036 6:28:16 UTC onwards). */
if( pSntpTime->seconds <= SNTP_TIME_AT_LARGEST_UNIX_TIME_SECS )
{
/* Unix Time ( seconds ) = Seconds Duration in
* [UNIX epoch, SNTP Era 1 Epoch Time]
* +
* Sntp Time since Era 1 Epoch
*/
*pUnixTimeSecs = UNIX_TIME_SECS_AT_SNTP_ERA_1_SMALLEST_TIME + pSntpTime->seconds;
}
/* Handle case when SNTP timestamp is in SNTP era 1 time range. */
else
{
*pUnixTimeSecs = pSntpTime->seconds - SNTP_TIME_AT_UNIX_EPOCH_SECS;
}
/* Convert SNTP fractions to microseconds for UNIX time. */
*pUnixTimeMicrosecs = pSntpTime->fractions / SNTP_FRACTION_VALUE_PER_MICROSECOND;
}
return status;
}

View File

@ -0,0 +1,655 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_client.h
* @brief API of an SNTPv4 client library that can send time requests and receive time response to/from
* SNTP/NTP servers. The library follows the Best Practices suggested in the the SNTPv4 specification,
* [RFC 4330](https://tools.ietf.org/html/rfc4330).
* The library can be used to run an SNTP client in a dedicated deamon task to periodically synchronize
* time from the Internet.
*/
#ifndef CORE_SNTP_CLIENT_H_
#define CORE_SNTP_CLIENT_H_
/* Standard include. */
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
/* Include coreSNTP Serializer header. */
#include "core_sntp_serializer.h"
/* *INDENT-OFF* */
#ifdef __cplusplus
extern "C" {
#endif
/* *INDENT-ON* */
/**
* @ingroup sntp_constants
* @brief The default UDP port supported by SNTP/NTP servers for client-server
* communication.
*
* @note It is possible for a server to use a different port number than
* the default port when using the Network Time Security protocol as the security
* mechanism for SNTP communication. For more information, refer to Section 4.1.8
* of [RFC 8915](https://tools.ietf.org/html/rfc8915).
*/
#define SNTP_DEFAULT_SERVER_PORT ( 123U )
/**
* @ingroup sntp_struct_types
* @brief Structure representing information for a time server.
*/
typedef struct SntpServerInfo
{
const char * pServerName; /**<@brief The time server name. */
size_t serverNameLen; /**<@brief The length of the server name.*/
uint16_t port; /**<@brief The UDP port supported by the server
* for SNTP/NTP communication. */
} SntpServerInfo_t;
/**
* @ingroup sntp_callback_types
* @brief Interface for user-defined function to resolve time server domain-name
* to an IPv4 address.
* The SNTP client library attempts to resolve the DNS of the time-server being
* used every time the @ref Sntp_SendTimeRequest API is called.
*
* @param[in] pServerAddr The time-server whose IPv4 address is to be resolved.
* @param[out] pIpV4Addr This should be filled with the resolved IPv4 address.
* of @p pTimeServer.
*
* @return `true` if DNS resolution is successful; otherwise `false` to represent
* failure.
*/
typedef bool ( * SntpResolveDns_t )( const SntpServerInfo_t * pServerAddr,
uint32_t * pIpV4Addr );
/**
* @ingroup sntp_callback_types
* @brief Interface for user-defined function to obtain the current system time
* in SNTP timestamp format.
*
* @note If your platform follows UNIX representation of time, the
* #SNTP_TIME_AT_UNIX_EPOCH_SECS and #SNTP_FRACTION_VALUE_PER_MICROSECOND macros
* can be used to convert UNIX time to SNTP timestamp.
*
* @param[out] pCurrentTime This should be filled with the current system time
* in SNTP timestamp format.
*
* @return `true` if obtaining system time is successful; otherwise `false` to
* represent failure.
*/
typedef void ( * SntpGetTime_t )( SntpTimestamp_t * pCurrentTime );
/**
* @ingroup sntp_callback_types
* @brief Interface for user-defined function to update the system clock time
* so that it is synchronized the time server used for getting current time.
*
* @param[in] pTimeServer The time server used to request time.
* @param[in] pServerTime The current time returned by the @p pTimeServer.
* @param[in] clockOffsetMs The calculated clock offset (in milliseconds) of the
* system relative to the server time.
* @param[in] leapSecondInfo Information about whether there is about an upcoming
* leap second adjustment of insertion or deletion in the last minute before midnight
* on the last day of the current month. For more information on leap seconds, refer
* to https://www.nist.gov/pml/time-and-frequency-division/leap-seconds-faqs. Depending
* on the accuracy requirements of the system clock, the user can choose to account
* for the leap second or ignore it in their system clock update logic.
*
* @note If the @p clockOffsetMs is positive, then the system time is BEHIND the server time,
* and if the @p clockOffsetMs, the system time is AHEAD of the server time. To correct the
* system time, the user can use either of "step", "slew" OR combination of the two clock
* discipline methodologies depending on the application needs.
* If the application requires a smooth time continuum of system time, then the "slew"
* discipline methodology can be used with the clock offset value, @p clockOffSetMs, to correct
* the system clock gradually with a "slew rate".
* If the application can accept sudden jump in time (forward or backward), then
* the "step" discipline methodology can be used to directly update the system
* clock with the current server time, @p pServerTime, every time the coreSNTP library
* calls the interface.
*/
typedef void ( * SntpSetTime_t )( const SntpServerInfo_t * pTimeServer,
const SntpTimestamp_t * pServerTime,
int64_t clockOffsetMs,
SntpLeapSecondInfo_t leapSecondInfo );
/**
* @ingroup sntp_struct_types
* @typedef NetworkContext_t
* @brief A user-defined type for context that is passed to the transport interface functions.
* It MUST be defined by the user to use the library.
* It is of incomplete type to allow user to define to the needs of their transport
* interface implementation.
*/
struct NetworkContext;
typedef struct NetworkContext NetworkContext_t;
/**
* @ingroup sntp_callback_types
* @brief Interface for user-defined function to send time request as a single datagram
* to server on the network over User Datagram Protocol (UDP).
*
* @note It is RECOMMENDED that the send operation is non-blocking, i.e. it
* SHOULD immediately return when the entire UDP data cannot be sent over the
* network. In such a case, the coreSNTP library will re-try send operation
* for a maximum period of blocking time passed to the @ref Sntp_SendTimeRequest API.
*
* @note If the size of the SNTP packet exceeds the Maximum Transmission Unit (MTU)
* supported by the network interface of the device, the user-defined implementation
* MAY either support fragmentation of UDP packets OR use a size for authentication data
* that allows the SNTP packet to fit within the MTU required size threshold. (Note that
* the size of SNTP packet is #SNTP_PACKET_BASE_SIZE + authentication data.)
*
* @param[in,out] pNetworkContext The user defined NetworkContext_t which
* is opaque to the coreSNTP library.
* @param[in] serverAddr The IPv4 address of the time server to send the data to.
* @param[in] serverPort The port of the server to send data to.
* @param[in] pBuffer The buffer containing the data to send over the network.
* @param[in] bytesToSend The size of data in @p pBuffer to send.
*
* @return The function SHOULD return one of the following integer codes:
* - @p bytesToSend when all requested data is successfully transmitted over the
* network.
* - 0 when no data could be sent over the network (due to network buffer being
* full, for example), and the send operation can be retried.
* - < 0 when the send operation failed to send any data due to an internal error,
* and operation cannot be retried.
*/
typedef int32_t ( * UdpTransportSendTo_t )( NetworkContext_t * pNetworkContext,
uint32_t serverAddr,
uint16_t serverPort,
const void * pBuffer,
uint16_t bytesToSend );
/**
* @ingroup sntp_callback_types
* @brief Interface for user-defined function to receive the server response, to a time
* request (sent through the @ref UdpTransportSendTo_t function), from the network over
* User Datagram Protocol (UDP).
*
* @note It is RECOMMENDED that the read operation is non-blocking, i.e. it
* SHOULD immediately return when no data is available on the network.
* In such a case, the coreSNTP library will re-try send operation for a maximum period
* of blocking time passed through the @ref Sntp_ReceiveTimeResponse API.
*
* @note If the size of the SNTP response packet from the server exceeds the
* Maximum Transmission Unit (MTU) supported by the network interface of the device,
* the user-defined implementation of the interface MAY either support receiving and
* assembling fragmented UDP packets OR use an authentication data size that allows
* SNTP packet to fit within the MTU required packet size threshold. (Note that
* the size of SNTP packet is #SNTP_PACKET_BASE_SIZE + authentication data.)
*
* @param[in,out] pNetworkContext The user defined NetworkContext_t which
* is opaque to the coreSNTP library.
* @param[in] serverAddr The IPv4 address of the time server to receive data from.
* @param[in] serverPort The port of the server to receive data from.
* @param[out] pBuffer This SHOULD be filled with data received from the network.
* @param[in] bytesToRecv The expected number of bytes to receive from the
* server.
*
* @return The function SHOULD return one of the following integer codes:
* - @p bytesToRecv value if all the requested number of bytes are received
* from the network.
* - ZERO when no data is available on the network, and the operation can be
* retried.
* - < 0 when the read operation failed due to internal error, and operation cannot
* be retried.
*/
typedef int32_t ( * UdpTransportRecvFrom_t )( NetworkContext_t * pNetworkContext,
uint32_t serverAddr,
uint16_t serverPort,
void * pBuffer,
uint16_t bytesToRecv );
/**
* @ingroup sntp_struct_types
* @brief Struct representing the UDP transport interface for user-defined functions
* that coreSNTP library depends on for performing read/write network operations.
*/
typedef struct UdpTransportIntf
{
NetworkContext_t * pUserContext; /**<@brief The user-defined context for storing
* network socket information. */
UdpTransportSendTo_t sendTo; /**<@brief The user-defined UDP send function. */
UdpTransportRecvFrom_t recvFrom; /**<@brief The user-defined UDP receive function. */
} UdpTransportInterface_t;
/**
* @ingroup sntp_struct_types
* @typedef SntpAuthContext_t
* @brief A user-defined type for context that is passed to the authentication interface functions.
* It MUST be defined by the user to use the library.
* It is of incomplete type to allow user to defined to the the needs of their authentication
* interface implementation.
*/
struct SntpAuthContext;
typedef struct SntpAuthContext SntpAuthContext_t;
/**
* @ingroup sntp_callback_types
* @brief Interface for user-defined function to generate and append
* authentication code in an SNTP request buffer for the SNTP client to be
* authenticated by the time server, if a security mechanism is used.
*
* The user can choose to implement with any security mechanism, symmetric
* key-based (like AES-CMAC) or asymmetric key-based (like Network Time Security),
* depending on the security mechanism supported by the time server being used
* to synchronize time with.
*
* @note The function SHOULD generate the authentication data for the first
* #SNTP_PACKET_BASE_SIZE bytes of SNTP request packet present in the passed buffer
* @p pBuffer, and fill the generated authentication data after #SNTP_PACKET_BASE_SIZE
* bytes in the buffer.
*
* @param[in,out] pContext The user defined NetworkContext_t which
* is opaque to the coreSNTP library.
* @param[in] pTimeServer The time server being used to request time from.
* This parameter is useful to choose the security mechanism when multiple time
* servers are configured in the library, and they require different security
* mechanisms or authentication credentials to use.
* @param[in, out] pBuffer This buffer SHOULD be filled with the authentication
* code generated from the #SNTP_PACKET_BASE_SIZE bytes of SNTP request data
* present in it.
* @param[in] bufferSize The maximum amount of data that can be held by the buffer,
* @p pBuffer.
* @param[out] pAuthCodeSize This should be filled with size of the authentication
* data appended to the SNTP request buffer, @p pBuffer. This value plus
* #SNTP_PACKET_BASE_SIZE should not exceed the buffer size, @p bufferSize.
*
* @return The function SHOULD return one of the following integer codes:
* - #SntpSuccess when the authentication data is successfully appended to @p pBuffer.
* - #SntpErrorBufferTooSmall when the user-supplied buffer (to the SntpContext_t through
* @ref Sntp_Init) is not large enough to hold authentication data.
* - #SntpErrorAuthFailure for failure to generate authentication data due to internal
* error.
*/
typedef SntpStatus_t (* SntpGenerateAuthCode_t )( SntpAuthContext_t * pContext,
const SntpServerInfo_t * pTimeServer,
void * pBuffer,
size_t bufferSize,
uint16_t * pAuthCodeSize );
/**
* @ingroup sntp_callback_types
* @brief Interface for user-defined function to authenticate server by validating
* the authentication code present in its SNTP response to a time request, if
* a security mechanism is supported by the server.
*
* The user can choose to implement with any security mechanism, symmetric
* key-based (like AES-CMAC) or asymmetric key-based (like Network Time Security),
* depending on the security mechanism supported by the time server being used
* to synchronize time with.
*
* @note In an SNTP response, the authentication code is present only after the
* first #SNTP_PACKET_BASE_SIZE bytes. Depending on the security mechanism used,
* the first #SNTP_PACKET_BASE_SIZE bytes MAY be used in validating the
* authentication data sent by the server.
*
* @param[in,out] pContext The user defined NetworkContext_t which
* is opaque to the coreSNTP library.
* @param[in] pTimeServer The time server that has to be authenticated from its
* SNTP response.
* This parameter is useful to choose the security mechanism when multiple time
* servers are configured in the library, and they require different security
* mechanisms or authentication credentials to use.
* @param[in] pResponseData The SNTP response from the server that contains the
* authentication code after the first #SNTP_PACKET_BASE_SIZE bytes.
* @param[in] responseSize The total size of the response from the server.
*
* @return The function SHOULD return one of the following integer codes:
* - #SntpSuccess when the server is successfully authenticated.
* - #SntpServerNotAuthenticated when server could not be authenticated.
* - #SntpErrorAuthFailure for failure to authenticate server due to internal
* error.
*/
typedef SntpStatus_t (* SntpValidateServerAuth_t )( SntpAuthContext_t * pContext,
const SntpServerInfo_t * pTimeServer,
const void * pResponseData,
uint16_t responseSize );
/**
* @ingroup sntp_struct_types
* @brief Struct representing the authentication interface for securely
* communicating with time servers.
*
* @note Using a security mechanism is OPTIONAL for using the coreSNTP
* library i.e. a user does not need to define the authentication interface
* if they are not using a security mechanism for SNTP communication.
*/
typedef struct SntpAuthenticationIntf
{
/**
*@brief The user-defined context for storing information like
* key credentials required for cryptographic operations in the
* security mechanism used for communicating with server.
*/
SntpAuthContext_t * pAuthContext;
/**
* @brief The user-defined function for appending client authentication data.
* */
SntpGenerateAuthCode_t generateClientAuth;
/**
* @brief The user-defined function for authenticating server from its SNTP
* response.
*/
SntpValidateServerAuth_t validateServerAuth;
} SntpAuthenticationInterface_t;
/**
* @ingroup sntp_struct_types
* @brief Structure for a context that stores state for managing a long-running
* SNTP client that periodically polls time and synchronizes system clock.
*/
typedef struct SntpContext
{
/**
* @brief List of time servers in decreasing priority order configured
* for the SNTP client.
* Only a single server is configured for use at a time across polling
* attempts until the server rejects a time request or there is a response
* timeout, after which, the next server in the list is used for subsequent
* polling requests.
*/
const SntpServerInfo_t * pTimeServers;
/**
* @brief Number of servers configured for use.
*/
size_t numOfServers;
/**
* @brief The index for the currently configured time server for time querying
* from the list of time servers in @ref pTimeServers.
*/
size_t currentServerIndex;
/**
* @brief The user-supplied buffer for storing network data of both SNTP requests
* and SNTP response.
*/
uint8_t * pNetworkBuffer;
/**
* @brief The size of the network buffer.
*/
size_t bufferSize;
/**
* @brief The user-supplied function for resolving DNS name of time servers.
*/
SntpResolveDns_t resolveDnsFunc;
/**
* @brief The user-supplied function for obtaining the current system time.
*/
SntpGetTime_t getTimeFunc;
/**
* @brief The user-supplied function for correcting system time after receiving
* time from a server.
*/
SntpSetTime_t setTimeFunc;
/**
* @brief The user-defined interface for performing User Datagram Protocol (UDP)
* send and receive network operations.
*/
UdpTransportInterface_t networkIntf;
/**
* @brief The user-defined interface for incorporating security mechanism of
* adding client authentication in SNTP request as well as authenticating server
* from SNTP response.
*
* @note If the application will not use security mechanism for any of the
* configured servers, then this interface can be undefined.
*/
SntpAuthenticationInterface_t authIntf;
/**
* @brief Cache of the resolved Ipv4 address of the current server being used for
* time synchronization.
* As a Best Practice functionality, the client library attempts to resolve the
* DNS of the time-server every time the @ref Sntp_SendTimeRequest API is called.
*/
uint32_t currentServerAddr;
/**
* @brief Cache of the timestamp of sending the last time request to a server
* for replay attack protection by checking that the server response contains
* the same timestamp in its "originate timestamp" field.
*/
SntpTimestamp_t lastRequestTime;
/**
* @brief State member for storing the size of the SNTP packet that includes
* both #SNTP_PACKET_BASE_SIZE bytes plus any authentication data, if a security
* mechanism is used.
* This value is used for expecting the same size for an SNTP response
* from the server.
*/
uint16_t sntpPacketSize;
/**
* @brief The timeout duration (in milliseconds) for receiving a response, through
* @ref Sntp_ReceiveTimeResponse API, from a server after the request for time is
* sent to it through @ref Sntp_SendTimeRequest API.
*/
uint32_t responseTimeoutMs;
} SntpContext_t;
/**
* @brief Initializes a context for SNTP client communication with SNTP/NTP
* servers.
*
* @param[out] pContext The user-supplied memory for the context that will be
* initialized to represent an SNTP client.
* @param[in] pTimeServers The list of decreasing order of priority of time
* servers that should be used by the SNTP client. This list MUST stay in
* scope for all the time of use of the context.
* @param[in] numOfServers The number of servers in the list, @p pTimeServers.
* @param[in] serverResponseTimeoutMs The timeout duration (in milliseconds) for
* receiving server response for time requests. The same timeout value is used for
* each server in the @p pTimeServers list.
* @param[in] pNetworkBuffer The user-supplied memory that will be used for
* storing network data for SNTP client-server communication. The buffer
* MUST stay in scope for all the time of use of the context.
* @param[in] bufferSize The size of the passed buffer @p pNetworkBuffer. The buffer
* SHOULD be appropriately sized for storing an entire SNTP packet which includes
* both #SNTP_PACKET_BASE_SIZE bytes of standard SNTP packet size, and space for
* authentication data, if security mechanism is used to communicate with any of
* the time servers configured for use.
* @param[in] resolveDnsFunc The user-defined function for DNS resolution of time
* server.
* @param[in] getSystemTimeFunc The user-defined function for querying system
* time.
* @param[in] setSystemTimeFunc The user-defined function for correcting system
* time for every successful time response received from a server.
* @param[in] pTransportIntf The user-defined function for performing network
* send/recv operations over UDP.
* @param[in] pAuthIntf The user-defined interface for generating client authentication
* in SNTP requests and authenticating servers in SNTP responses, if security mechanism
* is used in SNTP communication with server(s). If security mechanism is not used in
* communication with any of the configured servers (in @p pTimeServers), then the
* @ref SntpAuthenticationInterface_t does not need to be defined and this parameter
* can be NULL.
*
* @return This function returns one of the following:
* - #SntpSuccess if the context is initialized.
* - #SntpErrorBadParameter if any of the passed parameters in invalid.
* - #SntpErrorBufferTooSmall if the buffer does not have the minimum size
* required for a valid SNTP response packet.
*/
/* @[define_sntp_init] */
SntpStatus_t Sntp_Init( SntpContext_t * pContext,
const SntpServerInfo_t * pTimeServers,
size_t numOfServers,
uint32_t serverResponseTimeoutMs,
uint8_t * pNetworkBuffer,
size_t bufferSize,
SntpResolveDns_t resolveDnsFunc,
SntpGetTime_t getSystemTimeFunc,
SntpSetTime_t setSystemTimeFunc,
const UdpTransportInterface_t * pTransportIntf,
const SntpAuthenticationInterface_t * pAuthIntf );
/* @[define_sntp_init] */
/**
* @brief Sends a request for time from the currently configured server (in the
* context).
* If the user has provided an authentication interface, the client
* authentication code is appended to the request before sending over the network
* by calling the @ref SntpGenerateAuthCode_t function of the
* @ref SntpAuthenticationInterface_t.
*
* @note This function will ONLY send a request if there is a server available
* in the configured list that has not rejected an earlier request for time.
* This adheres to the Best Practice functionality specified in [Section 10 Point 8
* of SNTPv4 specification](https://tools.ietf.org/html/rfc4330#section-10).
*
* @param[in] pContext The context representing an SNTPv4 client.
* @param[in] randomNumber A random number serializing the SNTP request packet
* to protect against spoofing attacks by attackers that are off the network path
* of the SNTP client-server communication. This mechanism is suggested by SNTPv4
* specification in [RFC 4330 Section 3](https://tools.ietf.org/html/rfc4330#section-3).
* @param[in] blockTimeMs The maximum duration of time (in milliseconds) the function will
* block on attempting to send time request to the server over the network. If a zero
* block time value is provided, then the function will attempt to send the packet ONLY
* once.
*
* @note It is RECOMMENDED that a True Random Number Generator is used to generate the
* random number by using a hardware module, like Hardware Security Module (HSM), Secure Element,
* etc, as the entropy source.
*
* @return The API function returns one of the following:
* - #SntpSuccess if a time request is successfully sent to the currently configured
* time server in the context.
* - #SntpErrorBadParameter if an invalid context is passed to the function.
* - #SntpErrorContextNotInitialized if an uninitialized or invalid context is passed
* to the function.
* - #SntpErrorDnsFailure if there is failure in the user-defined function for
* DNS resolution of the time server.
* - #SntpErrorNetworkFailure if the SNTP request could not be sent over the network
* through the user-defined transport interface.
* - #SntpErrorAuthFailure if there was a failure in generating the client
* authentication code in the user-defined authentication interface.
* - #SntpErrorSendTimeout if the time request packet could not be sent over the
* network for the entire @p blockTimeMs duration.
*/
/* @[define_sntp_sendtimerequest] */
SntpStatus_t Sntp_SendTimeRequest( SntpContext_t * pContext,
uint32_t randomNumber,
uint32_t blockTimeMs );
/* @[define_sntp_sendtimerequest] */
/**
* @brief Receives a time response from the server that has been requested for time with
* the @ref Sntp_SendTimeRequest API function.
* Once an accepted response containing time from server is received, this function calls
* the user-defined @ref SntpSetTime_t function to update the system time.
*
* @note If the user has provided an authentication interface to the client
* (through @ref Sntp_Init), the server response is authenticated by calling the
* @ref SntpValidateServerAuth_t function of the @ref SntpAuthenticationInterface_t interface.
*
* @note On receiving a successful server response containing server time,
* this API calculates the clock offset value of the system clock relative to the
* server before calling the user-defined @ref SntpSetTime_t function for updating
* the system time.
*
* @note For correct calculation of clock-offset, the server and client times MUST be within
* ~68 years (or 2^31 seconds) of each other. In the special case when the server and client
* times are exactly 2^31 seconds apart, the library ASSUMES that the server time is ahead
* of the client time, and returns the positive clock-offset value of INT32_MAX seconds.
*
* @note This API will rotate the server of use in the library for the next time request
* (through the @ref Sntp_SendTimeRequest) if either of following events occur:
* - The server has responded with a rejection for the time request.
* OR
* - The server response wait has timed out.
* If all the servers configured in the context have been used, the API will rotate server for
* time query back to the first server in the list which will be used in next time request.
*
* @param[in] pContext The context representing an SNTPv4 client.
* @param[in] blockTimeMs The maximum duration of time (in milliseconds) the function will
* block on receiving a response from the server unless either the response is received
* OR a response timeout occurs.
*
* @note This function can be called multiple times with zero or small blocking times
* to poll whether server response is received until either the response response is
* received from the server OR a response timeout has occurred.
*
* @return This API functions returns one of the following:
* - #SntpSuccess if a successful server response is received.
* - #SntpErrorContextNotInitialized if an uninitialized or invalid context is passed
* to the function.
* - #SntpErrorBadParameter if an invalid context is passed to the function.
* - #SntpErrorNetworkFailure if there is a failure in the user-defined transport
* - #SntpErrorAuthFailure if an internal error occurs in the user-defined
* authentication interface when validating the server response.
* - #SntpServerNotAuthenticated when the server could not be authenticated from
* its response with the user-defined authentication interface.
* - #SntpInvalidResponse if the server response fails sanity checks expected in an
* SNTP response packet.
* - #SntpErrorResponseTimeout if a timeout has occurred in receiving response from
* the server.
* - #SntpRejectedResponse if the server responded with a rejection for the time
* request.
*/
/* @[define_sntp_receivetimeresponse] */
SntpStatus_t Sntp_ReceiveTimeResponse( SntpContext_t * pContext,
uint32_t blockTimeMs );
/* @[define_sntp_receivetimeresponse] */
/**
* @brief Converts @ref SntpStatus_t to its equivalent
* string.
*
* @note The returned string MUST NOT be modified.
*
* @param[in] status The status to convert to a string.
*
* @return The string representation of the status
* code.
*/
/* @[define_sntp_statustostr] */
const char * Sntp_StatusToStr( SntpStatus_t status );
/* @[define_sntp_statustostr] */
/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif
/* *INDENT-ON* */
#endif /* ifndef CORE_SNTP_CLIENT_H_ */

View File

@ -0,0 +1,144 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_config_defaults.h
* @brief This file represents the default values for the configuration macros
* of the coreSNTP library.
*
* @note This file SHOULD NOT be modified. If custom values are needed for
* any configuration macro, a core_sntp_config.h file should be provided to
* the SNTP library to override the default values defined in this file.
* To build the library with the core_sntp_config.h file, make sure to
* not set the SNTP_DO_NOT_USE_CUSTOM_CONFIG preprocessor macro.
*/
#ifndef CORE_SNTP_CONFIG_DEFAULTS_H_
#define CORE_SNTP_CONFIG_DEFAULTS_H_
/* The macro definition for SNTP_DO_NOT_USE_CUSTOM_CONFIG is for Doxygen
* documentation only. */
/**
* @brief Define this macro to build the SNTP library without the custom config
* file core_sntp_config.h.
*
* Without the custom config, the SNTP library builds with
* default values of config macros defined in core_sntp_config_defaults.h file.
*
* If a custom config is provided, then SNTP_DO_NOT_USE_CUSTOM_CONFIG should not
* be defined.
*/
#ifdef DOXYGEN
#define SNTP_DO_NOT_USE_CUSTOM_CONFIG
#endif
/* SNTP_DO_NOT_USE_CUSTOM_CONFIG allows building the SNTP library
* without a custom config. If a custom config is provided, the
* SNTP_DO_NOT_USE_CUSTOM_CONFIG macro should not be defined. */
#ifndef SNTP_DO_NOT_USE_CUSTOM_CONFIG
#include "core_sntp_config.h"
#endif
/**
* @brief Macro that is called in the SNTP library for logging "Error" level
* messages.
*
* To enable error level logging in the SNTP library, this macro should be mapped to the
* application-specific logging implementation that supports error logging.
*
* @note This logging macro is called in the SNTP library with parameters wrapped in
* double parentheses to be ISO C89/C90 standard compliant. For a reference
* POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
* logging-stack in demos folder of the
* [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C).
*
* <b>Default value</b>: Error logging is turned off, and no code is generated for calls
* to the macro in the SNTP library on compilation.
*/
#ifndef LogError
#define LogError( message )
#endif
/**
* @brief Macro that is called in the SNTP library for logging "Warning" level
* messages.
*
* To enable warning level logging in the SNTP library, this macro should be mapped to the
* application-specific logging implementation that supports warning logging.
*
* @note This logging macro is called in the SNTP library with parameters wrapped in
* double parentheses to be ISO C89/C90 standard compliant. For a reference
* POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
* logging-stack in demos folder of the
* [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
*
* <b>Default value</b>: Warning logs are turned off, and no code is generated for calls
* to the macro in the SNTP library on compilation.
*/
#ifndef LogWarn
#define LogWarn( message )
#endif
/**
* @brief Macro that is called in the SNTP library for logging "Info" level
* messages.
*
* To enable info level logging in the SNTP library, this macro should be mapped to the
* application-specific logging implementation that supports info logging.
*
* @note This logging macro is called in the SNTP library with parameters wrapped in
* double parentheses to be ISO C89/C90 standard compliant. For a reference
* POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
* logging-stack in demos folder of the
* [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
*
* <b>Default value</b>: Info logging is turned off, and no code is generated for calls
* to the macro in the SNTP library on compilation.
*/
#ifndef LogInfo
#define LogInfo( message )
#endif
/**
* @brief Macro that is called in the SNTP library for logging "Debug" level
* messages.
*
* To enable debug level logging from SNTP library, this macro should be mapped to the
* application-specific logging implementation that supports debug logging.
*
* @note This logging macro is called in the SNTP library with parameters wrapped in
* double parentheses to be ISO C89/C90 standard compliant. For a reference
* POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
* logging-stack in demos folder of the
* [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
*
* <b>Default value</b>: Debug logging is turned off, and no code is generated for calls
* to the macro in the SNTP library on compilation.
*/
#ifndef LogDebug
#define LogDebug( message )
#endif
#endif /* ifndef CORE_SNTP_CONFIG_DEFAULTS_H_ */

View File

@ -0,0 +1,535 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_serializer.h
* @brief API for serializing SNTP request packets and, and de-serializing SNTP
* response packets.
* This API layer adheres to the SNTPv4 specification defined in
* [RFC 4330](https://tools.ietf.org/html/rfc4330).
*/
#ifndef CORE_SNTP_SERIALIZER_H_
#define CORE_SNTP_SERIALIZER_H_
/* Standard include. */
#include <stdint.h>
#include <stddef.h>
/* *INDENT-OFF* */
#ifdef __cplusplus
extern "C" {
#endif
/* *INDENT-ON* */
/**
* @ingroup sntp_constants
* @brief The base packet size of request and response of the (S)NTP protocol.
* @note This is the packet size without any authentication headers for security
* mechanism. If the application uses a security mechanism for communicating with
* an (S)NTP server, it can add authentication data after the SNTP packet is
* serialized with the @ref Sntp_SerializeRequest API function.
*/
#define SNTP_PACKET_BASE_SIZE ( 48U )
/**
* @ingroup sntp_constants
* @brief Number of SNTP timestamp fractions in 1 microsecond.
*
* The fraction's part of an SNTP timestamp is 32-bits wide, thereby, giving a
* resolution of 2^(-32) seconds ~ 232 picoseconds.
*
* @note The application can use this value to convert microseconds part of system
* time into SNTP timestamp fractions. For example, if the microseconds
* part of system time is n microseconds, the fractions value to be used for the
* SNTP timestamp part will be n * SNTP_FRACTION_VALUE_PER_MICROSECOND.
*/
#define SNTP_FRACTION_VALUE_PER_MICROSECOND ( 4295U )
/**
* @ingroup sntp_constants
* @brief The seconds part of SNTP time at the UNIX epoch time, which represents
* an offset of 70 years (in seconds) between SNTP epoch and UNIX epoch time.
* SNTP uses 1st Jan 1900 UTC as the epoch time, whereas UNIX standard uses
* 1st Jan 1970 UTC as the epoch time, thereby, causing an offset of 70 years
* between them.
*
* Difference of 70 years = ((70 * 365) + 17 leap days) * 24 * 3600 seconds
*
* @note If your system follows UNIX time, the application can use this value to
* convert seconds part of a system time to seconds part of the equivalent SNTP
* time. For example, if the seconds part of system time is n seconds, the seconds
* value to be used for the SNTP timestamp will be n + SNTP_TO_UNIX_OFFSET_SECS.
*/
#define SNTP_TIME_AT_UNIX_EPOCH_SECS ( 2208988800U )
/**
* @ingroup sntp_constants
* @brief The seconds value of SNTP timestamp for the largest UNIX time when
* using signed 32-bit integer for seconds.
* The largest time representable with a 32-bit signed integer in UNIX time
* is 19 Jan 2038 3h 14m 7s UTC. However, as the SNTP time overflows at
* 7 Feb 2036 6h 28m 16s UTC, therefore, the SNTP time for the largest UNIX time
* represents the time duration between the 2 timestamps.
*
* SNTP Time at Largest Time Duration in the range
* Signed 32-bit UNIX time = [7 Feb 2036 6:28:16, 19 Jan 2038 3:14:07]
*/
#define SNTP_TIME_AT_LARGEST_UNIX_TIME_SECS ( 61505151U )
/**
* @ingroup sntp_constants
* @brief The UNIX time (in seconds) at the smallest SNTP time in era 1,
* i.e. UNIX time at 7 Feb 2036 6:28:16 UTC/
*
* Time Duration = 7 Feb 6:28:16 UTC (SNTP Era 1 Epoch) -
* 1 Jan 1970 0:0:0 UTC (UNIX epoch)
* = 66 years, 37 days, 6 hours, 28 minutes and 16 seconds
* = ((66 * 365) + 16 leap days) * 24 * 3600) + (6 * 3600)
* + (28 * 60) + 16
*/
#define UNIX_TIME_SECS_AT_SNTP_ERA_1_SMALLEST_TIME ( 2085978496U )
/**
* @ingroup sntp_constants
* @brief The fixed-length of any Kiss-o'-Death message ASCII code sent
* in an SNTP server response.
* @note An SNTP server sends a Kiss-o'-Death message to reject a time request
* from the client. For more information on the Kiss-o'-Death codes, refer to the
* [SNTPv4 specification Section 8](https://tools.ietf.org/html/rfc4330#section-8).
*/
#define SNTP_KISS_OF_DEATH_CODE_LENGTH ( 4U )
/**
* @ingroup sntp_constants
* @brief The value for the #SntpResponseData_t.rejectedResponseCode member
* when that the server response packet does not contain a Kiss-o'-Death
* message, and therefore, does not have a "kiss code".
* The server sends a "kiss-code" only when it rejects an SNTP request
* with a Kiss-o'-Death message.
*/
#define SNTP_KISS_OF_DEATH_CODE_NONE ( 0U )
/**
* @ingroup sntp_enum_types
* @brief Enumeration of status codes that can be returned
* by the coreSNTP Library API.
*/
typedef enum SntpStatus
{
/**
* @brief Successful operation of an SNTP API.
*/
SntpSuccess,
/**
* @brief Invalid parameter passed to an API function.
*/
SntpErrorBadParameter,
/**
* @brief Server sent a Kiss-o'-Death message to reject the request for time.
* This status can be returned by the @ref Sntp_ReceiveTimeResponse API.
*/
SntpRejectedResponse,
/**
* @brief Server sent a Kiss-o'-Death message with non-retryable code (i.e. DENY or RSTR).
*/
SntpRejectedResponseChangeServer,
/**
* @brief Server sent a Kiss-o'-Death message with a RATE code, which means that
* client should back-off before retrying.
*/
SntpRejectedResponseRetryWithBackoff,
/**
* @brief Server sent a Kiss-o'-Death message with a code, specific to the server.
* Application can inspect the ASCII kiss-code from @ref Sntp_DeserializeResponse API.
*/
SntpRejectedResponseOtherCode,
/**
* @brief Application provided insufficient buffer space for serializing
* or de-serializing an SNTP packet.
* The minimum size of an SNTP packet is #SNTP_PACKET_BASE_SIZE
* bytes. */
SntpErrorBufferTooSmall,
/**
* @brief Server response failed validation checks for expected data in SNTP packet.
*/
SntpInvalidResponse,
/**
* @brief Poll interval value is under 1 second which cannot be calculated
* by @ref Sntp_CalculatePollInterval.
*/
SntpZeroPollInterval,
/**
* @brief SNTP timestamp cannot be converted to UNIX time as time does not lie
* in time range supported by Sntp_ConvertToUnixTime.
*/
SntpErrorTimeNotSupported,
/**
* @brief The user-defined DNS resolution interface, @ref SntpResolveDns_t, failed to resolve
* address for a time server. This status is returned by the @ref Sntp_SendTimeRequest API.
*/
SntpErrorDnsFailure,
/**
* @brief Networking operation of sending or receiving SNTP packet through the user-defined UDP
* transport interface, @ref UdpTransportInterface_t, failed.
* This status is returned by either of @ref Sntp_SendTimeRequest OR @ref Sntp_ReceiveTimeResponse
* APIs.
*/
SntpErrorNetworkFailure,
/**
* @brief Time server is not authenticated from the authentication data in its response.
* This status can be returned by the user-supplied definition of the
* @ref SntpValidateServerAuth_t authentication interface.
*/
SntpServerNotAuthenticated,
/**
* @brief Failure from the user-supplied authentication interface, @ref SntpAuthenticationInterface_t,
* in either generating authentication data for SNTP request OR validating the authentication
* data in SNTP response from server.
*/
SntpErrorAuthFailure,
/**
* @brief A timeout occurred in sending time request packet over the network to a server through the
* @ref Sntp_SendTimeRequest API.
*/
SntpErrorSendTimeout,
/**
* @brief A timeout has occurred in receiving server response with the @ref Sntp_ReceiveTimeResponse
* API.
*/
SntpErrorResponseTimeout,
/**
* @brief No SNTP packet for server response is received from the network by the
* @ref Sntp_ReceiveTimeResponse API.
*/
SntpNoResponseReceived,
/**
* @brief The SNTP context passed to @ref Sntp_SendTimeRequest or @ref Sntp_ReceiveTimeResponse APIs is
* is uninitialized.
*/
SntpErrorContextNotInitialized
} SntpStatus_t;
/**
* @ingroup sntp_enum_types
* @brief Enumeration for leap second information that an SNTP server can
* send its response to a time request. An SNTP server sends information about
* whether there is an upcoming leap second adjustment in the last day of the
* current month.
*
* @note A leap second is an adjustment made in atomic clock time because Earth's rotation
* can be inconsistent. Leap seconds are usually incorporated as an extra second insertion
* or second deletion in the last minute before midnight i.e. in the minute of 23h:59m UTC
* on the last day of June or December. For more information on leap seconds, refer to
* https://www.nist.gov/pml/time-and-frequency-division/leap-seconds-faqs.
*/
typedef enum SntpLeapSecondInfo
{
NoLeapSecond = 0x00, /** <@brief There is no upcoming leap second adjustment. */
LastMinuteHas61Seconds = 0x01, /** <@brief A leap second should be inserted in the last minute before midnight. */
LastMinuteHas59Seconds = 0x02, /** <@brief A leap second should be deleted from the last minute before midnight. */
AlarmServerNotSynchronized = 0x03 /** <@brief An alarm condition meaning that server's time is not synchronized
* to an upstream NTP (or SNTP) server. */
} SntpLeapSecondInfo_t;
/**
* @ingroup sntp_struct_types
* @brief Structure representing an SNTP timestamp.
*
* @note The SNTP timestamp uses 1st January 1900 0h 0m 0s Coordinated Universal Time (UTC)
* as the primary epoch i.e. the timestamp represents current time as the amount of time since
* the epoch time.
* Refer to the [SNTPv4 specification](https://tools.ietf.org/html/rfc4330#section-3) for more
* information of the SNTP timestamp format.
*/
typedef struct SntpTimestamp
{
uint32_t seconds; /**< @brief Number of seconds since epoch time. */
uint32_t fractions; /**< @brief The fractions part of the SNTP timestamp with resolution
* of 2^(-32) ~ 232 picoseconds. */
} SntpTimestamp_t;
/**
* @ingroup sntp_struct_types
* @brief Structure representing data parsed from an SNTP response from server
* as well as data of arithmetic calculations derived from the response.
*/
typedef struct SntpResponse
{
/**
* @brief The timestamp sent by the server.
*/
SntpTimestamp_t serverTime;
/**
* @brief The information of an upcoming leap second in the
* server response.
*/
SntpLeapSecondInfo_t leapSecondType;
/**
* @brief If a server responded with Kiss-o'-Death message to reject
* time request, this is the fixed length ASCII code sequence for the
* rejection.
*
* The Kiss-o'-Death code is always #SNTP_KISS_OF_DEATH_CODE_LENGTH
* bytes long.
*
* @note If the server does not send a Kiss-o'-Death message in its
* response, this value will be #SNTP_KISS_OF_DEATH_CODE_NONE.
*/
uint32_t rejectedResponseCode;
/**
* @brief The offset (in milliseconds) of the system clock relative to the server time
* calculated from timestamps in the client SNTP request and server SNTP response packets.
* If the the system time is BEHIND the server time, then the clock-offset value is > 0.
* If the system time is AHEAD of the server time, then the clock-offset value is < 0.
*
* @note This information can be used to synchronize the system clock with a "slew",
* "step" OR combination of the two clock correction methodologies depending on the degree
* of system clock drift (represented by the clock-offset) and the application's
* tolerance for system clock error.
*
* @note The library calculates the clock-offset value using the On-Wire
* protocol suggested by the NTPv4 specification. For more information,
* refer to https://tools.ietf.org/html/rfc5905#section-8.
*
* @note The library ASSUMES that the server and client systems are within
* ~68 years of each other clock, whether in the same NTP era or across adjacent
* NTP eras. Thus, the client and system times MUST be within ~68 years (or
* 2^31 seconds exactly) of each other for correct calculation of clock-offset.
*
* @note When the server and client times are exactly 2^31 (or INT32_MAX + 1 )
* seconds apart, the library ASSUMES that the server time is ahead of the client
* time, and return the clock-offset value of INT32_MAX.
*/
int64_t clockOffsetMs;
} SntpResponseData_t;
/**
* @brief Serializes an SNTP request packet to use for querying a
* time server.
*
* This function will fill only #SNTP_PACKET_BASE_SIZE bytes of data in the
* passed buffer.
*
* @param[in, out] pRequestTime The current time of the system, expressed as time
* since the SNTP epoch (i.e. 0h of 1st Jan 1900 ). This time will be serialized
* in the SNTP request packet. The function will use this parameter to return the
* timestamp serialized in the SNTP request. To protect against attacks spoofing
* server responses, the timestamp MUST NOT be zero in value.
* @param[in] randomNumber A random number (generated by a True Random Generator)
* for use in the SNTP request packet to protect against replay attacks as suggested
* by SNTPv4 specification. For more information, refer to
* [RFC 4330 Section 3](https://tools.ietf.org/html/rfc4330#section-3).
* @param[out] pBuffer The buffer that will be populated with the serialized
* SNTP request packet.
* @param[in] bufferSize The size of the @p pBuffer buffer. It should be at least
* #SNTP_PACKET_BASE_SIZE bytes in size.
*
* @note It is recommended to use a True Random Generator (TRNG) to generate
* the random number.
* @note The application MUST save the @p pRequestTime value for de-serializing
* the server response with @ref Sntp_DeserializeResponse API.
*
* @return This function returns one of the following:
* - #SntpSuccess when serialization operation is successful.
* - #SntpErrorBadParameter if an invalid parameter is passed.
* - #SntpErrorBufferTooSmall if the buffer does not have the minimum size
* for serializing an SNTP request packet.
*/
/* @[define_sntp_serializerequest] */
SntpStatus_t Sntp_SerializeRequest( SntpTimestamp_t * pRequestTime,
uint32_t randomNumber,
void * pBuffer,
size_t bufferSize );
/* @[define_sntp_serializerequest] */
/**
* @brief De-serializes an SNTP packet received from a server as a response
* to a SNTP request.
*
* This function will parse only the #SNTP_PACKET_BASE_SIZE bytes of data
* in the passed buffer.
*
* @note If the server has sent a Kiss-o'-Death message to reject the associated
* time request, the API function will return the appropriate return code and,
* also, provide the ASCII code (of fixed length, #SNTP_KISS_OF_DEATH_CODE_LENGTH bytes)
* in the #SntpResponseData_t.rejectedResponseCode member of @p pParsedResponse parameter,
* parsed from the response packet.
* The application SHOULD respect the server rejection and take appropriate action
* based on the rejection code.
* If the server response represents an accepted SNTP client request, then the API
* function will set the #SntpResponseData_t.rejectedResponseCode member of
* @p pParsedResponse parameter to #SNTP_KISS_OF_DEATH_CODE_NONE.
*
* @note If the server has positively responded with its clock time, then this API
* function will calculate the clock-offset. For the clock-offset to be correctly
* calculated, the system clock MUST be within ~68 years (or 2^31 seconds) of the server
* time mentioned. This function supports clock-offset calculation when server and client
* timestamps are in adjacent NTP eras, with one system is in NTP era 0 (i.e. before 7 Feb 2036
* 6h:28m:14s UTC) and another system in NTP era 1 (on or after 7 Feb 2036 6h:28m:14s UTC).
*
* @note In the special case when the server and client times are exactly 2^31 seconds apart,
* the library ASSUMES that the server time is ahead of the client time, and returns the
* positive clock-offset value of INT32_MAX seconds.
*
* @param[in] pRequestTime The system time used in the SNTP request packet
* that is associated with the server response. This MUST be the same as the
* time returned by the @ref Sntp_SerializeRequest API. To protect against attacks
* spoofing server responses, this timestamp MUST NOT be zero in value.
* @param[in] pResponseRxTime The time of the system, expressed as time since the
* SNTP epoch (i.e. 0h of 1st Jan 1900 ), at receiving SNTP response from server.
* This time will be used to calculate system clock offset relative to server.
* @param[in] pResponseBuffer The buffer containing the SNTP response from the
* server.
* @param[in] bufferSize The size of the @p pResponseBuffer containing the SNTP
* response. It MUST be at least #SNTP_PACKET_BASE_SIZE bytes
* long for a valid SNTP response.
* @param[out] pParsedResponse The information parsed from the SNTP response packet.
* If possible to calculate without overflow, it also contains the system clock
* offset relative to the server time.
*
* @return This function returns one of the following:
* - #SntpSuccess if the de-serialization operation is successful.
* - #SntpErrorBadParameter if an invalid parameter is passed.
* - #SntpErrorBufferTooSmall if the buffer does not have the minimum size
* required for a valid SNTP response packet.
* - #SntpInvalidResponse if the response fails sanity checks expected in an
* SNTP response.
* - #SntpRejectedResponseChangeServer if the server rejected with a code
* indicating that client cannot be retry requests to it.
* - #SntpRejectedResponseRetryWithBackoff if the server rejected with a code
* indicating that client should back-off before retrying request.
* - #SntpRejectedResponseOtherCode if the server rejected with a code that
* application can inspect in the @p pParsedResponse parameter.
*/
/* @[define_sntp_deserializeresponse] */
SntpStatus_t Sntp_DeserializeResponse( const SntpTimestamp_t * pRequestTime,
const SntpTimestamp_t * pResponseRxTime,
const void * pResponseBuffer,
size_t bufferSize,
SntpResponseData_t * pParsedResponse );
/* @[define_sntp_deserializeresponse] */
/**
* @brief Utility to calculate the poll interval of sending periodic time queries
* to servers to achieve a desired system clock accuracy for a given
* frequency tolerance of the system clock.
*
* For example, from the SNTPv4 specification, "if the frequency tolerance
* is 200 parts per million (PPM) and the required accuracy is one minute,
* the maximum timeout is about 3.5 days". In this example, the system
* clock frequency tolerance is 200 PPM and the desired accuracy is
* 60000 milliseconds (or 1 minute) for which this API function
* will return the maximum poll interval value as 2^18 seconds (or ~3 days).
*
* @note The poll interval returned is a power of 2, which is the
* standard way to represent the value. According to the SNTPv4 specification
* Best Practices, an SNTP client SHOULD NOT have a poll interval less than 15 seconds.
* https://tools.ietf.org/html/rfc4330#section-10. This API function DOES NOT
* support poll interval calculation less than 1 second.
*
* @param[in] clockFreqTolerance The frequency tolerance of system clock
* in parts per million (PPM) units. This parameter MUST be non-zero.
* @param[in] desiredAccuracy The acceptable maximum drift, in milliseconds,
* for the system clock. The maximum value (0xFFFF) represents ~1 minute of
* desired clock accuracy. This parameter MUST be non-zero.
* @param[out] pPollInterval This is filled with the poll interval, in seconds
* calculated as the closest power of 2 value that will achieve either the
* exact desired or higher clock accuracy @p desiredAccuracy, for the given clock
* frequency tolerance, @p clockFreqTolerance.
*
* @return Returns one of the following:
* - #SntpSuccess if calculation is successful.
* - #SntpErrorBadParameter for an invalid parameter passed to the function.
* - #SntpZeroPollInterval if calculated poll interval is less than 1 second.
*/
/* @[define_sntp_calculatepollinterval] */
SntpStatus_t Sntp_CalculatePollInterval( uint16_t clockFreqTolerance,
uint16_t desiredAccuracy,
uint32_t * pPollInterval );
/* @[define_sntp_calculatepollinterval] */
/**
* @brief Utility to convert SNTP timestamp (that uses 1st Jan 1900 as the epoch) to
* UNIX timestamp (that uses 1st Jan 1970 as the epoch).
*
* @note This function can ONLY handle conversions of SNTP timestamps that lie in the
* range from 1st Jan 1970 0h 0m 0s, the UNIX epoch time, to 19th Jan 2038 3h 14m 7s,
* the maximum UNIX time that can be represented in a signed 32 bit integer. (The
* limitation is to support systems that use signed 32-bit integer to represent the
* seconds part of the UNIX time.)
*
* @note This function supports overflow of the SNTP timestamp (from the 7 Feb 2036
* 6h 28m 16s time, i.e. SNTP era 1) by treating the timestamps with seconds part
* in the range [0, 61,505,152] seconds where the upper limit represents the UNIX
* overflow time (i.e. 19 Jan 2038 3h 14m 7s) for systems that use signed 32-bit
* integer to represent time.
*
* @param[in] pSntpTime The SNTP timestamp to convert to UNIX time.
* @param[out] pUnixTimeSecs This will be filled with the seconds part of the
* UNIX time equivalent of the SNTP time, @p pSntpTime.
* @param[out] pUnixTimeMicrosecs This will be filled with the microseconds part
* of the UNIX time equivalent of the SNTP time, @p pSntpTime.
*
* @return Returns one of the following:
* - #SntpSuccess if conversion to UNIX time is successful
* - #SntpErrorBadParameter if any of the passed parameters are NULL.
* - #SntpErrorTimeNotSupported if the passed SNTP time does not lie in the
* supported time range.
*/
/* @[define_sntp_converttounixtime] */
SntpStatus_t Sntp_ConvertToUnixTime( const SntpTimestamp_t * pSntpTime,
uint32_t * pUnixTimeSecs,
uint32_t * pUnixTimeMicrosecs );
/* @[define_sntp_converttounixtime] */
/* *INDENT-OFF* */
#ifdef __cplusplus
}
#endif
/* *INDENT-ON* */
#endif /* ifndef CORE_SNTP_SERIALIZER_H_ */

View File

@ -0,0 +1,103 @@
cmake_minimum_required( VERSION 3.13.0 )
project( "coreSNTP unit test"
LANGUAGES C )
# Allow the project to be organized into folders.
set_property( GLOBAL PROPERTY USE_FOLDERS ON )
# Use C90.
set( CMAKE_C_STANDARD 90 )
set( CMAKE_C_STANDARD_REQUIRED ON )
# Do not allow in-source build.
if( ${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR} )
message( FATAL_ERROR "In-source build is not allowed. Please build in a separate directory, such as ${PROJECT_SOURCE_DIR}/build." )
endif()
# Set global path variables.
get_filename_component(__MODULE_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
set( MODULE_ROOT_DIR ${__MODULE_ROOT_DIR} CACHE INTERNAL "coreSNTP source root." )
set( UNIT_TEST_DIR ${MODULE_ROOT_DIR}/test/unit-test CACHE INTERNAL "coreSNTP unit test directory." )
set( CMOCK_DIR ${UNIT_TEST_DIR}/CMock CACHE INTERNAL "Unity library source directory." )
# Configure options to always show in CMake GUI.
option( BUILD_CLONE_SUBMODULES
"Set this to ON to automatically clone any required Git submodules. When OFF, submodules must be manually cloned."
OFF )
# Set output directories.
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
# ================================ Coverity Analysis Configuration =================================
# Include filepaths for source and include.
include( ${MODULE_ROOT_DIR}/coreSntpFilePaths.cmake )
# Target for Coverity analysis that builds the library.
add_library( coverity_analysis
${CORE_SNTP_SOURCES} )
# Add coreSNTP library public include path.
target_include_directories( coverity_analysis
PUBLIC
${CORE_SNTP_INCLUDE_PUBLIC_DIRS} )
# Build SNTP library target without custom config dependency.
target_compile_definitions( coverity_analysis PUBLIC SNTP_DO_NOT_USE_CUSTOM_CONFIG=1 )
# Build without debug enabled when performing static analysis
target_compile_options(coverity_analysis PUBLIC -DNDEBUG )
# ==================================== Code Example Build ====================================
if(${BUILD_CODE_EXAMPLE})
# Target for Coverity analysis that builds the library.
add_executable( code_example_posix
${MODULE_ROOT_DIR}/docs/doxygen/code_examples/example_sntp_client_posix.c )
# Add coreSNTP library public include path.
target_link_libraries( code_example_posix
coverity_analysis )
endif()
# ==================================== Unit Test Configuration ====================================
if(${BUILD_UNIT_TESTS})
# Include CMock build configuration.
include( unit-test/cmock_build.cmake )
# Check if the CMock source directory exists, and if not present, clone the submodule
# if BUILD_CLONE_SUBMODULES configuration is enabled.
if( NOT EXISTS ${CMOCK_DIR}/src )
# Attempt to clone CMock.
clone_cmock()
endif()
# Add unit test and coverage configuration.
# Use CTest utility for managing test runs. This has to be added BEFORE
# defining test targets with add_test()
enable_testing()
# Add build targets for CMock, required for unit testing.
add_cmock_targets()
# Add function to enable CMock/Unity based tests and coverage.
include( ${MODULE_ROOT_DIR}/tools/cmock/create_test.cmake )
# Include build configuration for unit tests.
add_subdirectory( unit-test )
endif()
# ==================================== Coverage Analysis configuration ============================
# Add a target for running coverage on tests.
add_custom_target( coverage
COMMAND ${CMAKE_COMMAND} -DCMOCK_DIR=${CMOCK_DIR}
-P ${MODULE_ROOT_DIR}/tools/cmock/coverage.cmake
DEPENDS cmock core_sntp_client_utest core_sntp_serializer_utest
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

View File

@ -0,0 +1,24 @@
# Emitted when running CBMC proofs
proofs/**/logs
proofs/**/gotos
proofs/**/report
proofs/**/html
proofs/output
# Emitted by CBMC Viewer
TAGS-*
# Emitted by Arpa
arpa_cmake/
arpa-validation-logs/
Makefile.arpa
# Emitted by litani
.ninja_deps
.ninja_log
.litani_cache_dir
# These files should be overwritten whenever prepare.py runs
cbmc-batch.yaml
__pycache__/

View File

@ -0,0 +1,6 @@
CBMC proof include files
========================
This directory contains include files written for CBMC proof. It is
common to write some code to model aspects of the system under test,
and the header files for this code go here.

View File

@ -0,0 +1,53 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_cbmc_state.h
* @brief Allocation and assumption utilities for the SNTP library CBMC proofs.
*/
#ifndef CORE_SNTP_CBMC_STATE_H_
#define CORE_SNTP_CBMC_STATE_H_
#include "core_sntp_client.h"
/* Application defined Network context. */
struct NetworkContext
{
void * networkContext;
};
/* Application defined authentication context. */
struct SntpAuthContext
{
void * authContext;
};
/**
* @brief Allocate a #SntpContext_t object.
*
* @return NULL or allocated #SntpContext_t memory.
*/
SntpContext_t * unconstrainedCoreSntpContext();
#endif /* ifndef CORE_SNTP_CBMC_STATE_H_ */

View File

@ -0,0 +1,177 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_config_defaults.h
* @brief This file represents the default values for the configuration macros
* of the coreSNTP library.
*
* @note This file SHOULD NOT be modified. If custom values are needed for
* any configuration macro, a core_sntp_config.h file should be provided to
* the SNTP library to override the default values defined in this file.
* To build the library with the core_sntp_config.h file, make sure to
* not set the SNTP_DO_NOT_USE_CUSTOM_CONFIG preprocessor macro.
*/
#ifndef CORE_SNTP_CONFIG_DEFAULTS_H_
#define CORE_SNTP_CONFIG_DEFAULTS_H_
/* The macro definition for SNTP_DO_NOT_USE_CUSTOM_CONFIG is for Doxygen
* documentation only. */
/**
* @brief Define this macro to build the SNTP library without the custom config
* file core_sntp_config.h.
*
* Without the custom config, the SNTP library builds with
* default values of config macros defined in core_sntp_config_defaults.h file.
*
* If a custom config is provided, then SNTP_DO_NOT_USE_CUSTOM_CONFIG should not
* be defined.
*/
#ifdef DOXYGEN
#define SNTP_DO_NOT_USE_CUSTOM_CONFIG
#endif
/**
* @brief The maximum duration between non-empty network reads while
* receiving an SNTP packet via the #Sntp_ReceiveTimeResponse API function.
*
* When an incoming SNTP packet is detected, the transport receive function
* may be called multiple times until all of the expected number of bytes of the
* packet are received. This timeout represents the maximum polling duration that
* is allowed without any data reception from the network for the incoming packet.
*
* If the timeout expires, the #Sntp_ReceiveTimeResponse function will return
* #SntpErrorNetworkFailure.
*
* <b>Possible values:</b> Any positive 16 bit integer. Recommended to use a
* small timeout value. <br>
* <b>Default value:</b> `10`
*/
#ifndef SNTP_RECV_POLLING_TIMEOUT_MS
#define SNTP_RECV_POLLING_TIMEOUT_MS ( 10U )
#endif
/**
* @brief The maximum duration between non-empty network transmissions while
* sending an SNTP packet via the #Sntp_SendTimeRequest API function.
*
* When sending an SNTP packet, the transport send function may be called multiple
* times until all of the required number of bytes are sent.
* This timeout represents the maximum duration that is allowed for no data
* transmission over the network through the transport send function.
*
* If the timeout expires, the #Sntp_SendTimeRequest function will return
* #SntpErrorNetworkFailure.
*
* <b>Possible values:</b> Any positive 16 bit integer. Recommended to use a small
* timeout value. <br>
* <b>Default value:</b> `10`
*/
#ifndef SNTP_SEND_RETRY_TIMEOUT_MS
#define SNTP_SEND_RETRY_TIMEOUT_MS ( 10U )
#endif
/**
* @brief Macro that is called in the SNTP library for logging "Error" level
* messages.
*
* To enable error level logging in the SNTP library, this macro should be mapped to the
* application-specific logging implementation that supports error logging.
*
* @note This logging macro is called in the SNTP library with parameters wrapped in
* double parentheses to be ISO C89/C90 standard compliant. For a reference
* POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
* logging-stack in demos folder of the
* [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C).
*
* <b>Default value</b>: Error logging is turned off, and no code is generated for calls
* to the macro in the SNTP library on compilation.
*/
#ifndef LogError
#define LogError( message )
#endif
/**
* @brief Macro that is called in the SNTP library for logging "Warning" level
* messages.
*
* To enable warning level logging in the SNTP library, this macro should be mapped to the
* application-specific logging implementation that supports warning logging.
*
* @note This logging macro is called in the SNTP library with parameters wrapped in
* double parentheses to be ISO C89/C90 standard compliant. For a reference
* POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
* logging-stack in demos folder of the
* [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
*
* <b>Default value</b>: Warning logs are turned off, and no code is generated for calls
* to the macro in the SNTP library on compilation.
*/
#ifndef LogWarn
#define LogWarn( message )
#endif
/**
* @brief Macro that is called in the SNTP library for logging "Info" level
* messages.
*
* To enable info level logging in the SNTP library, this macro should be mapped to the
* application-specific logging implementation that supports info logging.
*
* @note This logging macro is called in the SNTP library with parameters wrapped in
* double parentheses to be ISO C89/C90 standard compliant. For a reference
* POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
* logging-stack in demos folder of the
* [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
*
* <b>Default value</b>: Info logging is turned off, and no code is generated for calls
* to the macro in the SNTP library on compilation.
*/
#ifndef LogInfo
#define LogInfo( message )
#endif
/**
* @brief Macro that is called in the SNTP library for logging "Debug" level
* messages.
*
* To enable debug level logging from SNTP library, this macro should be mapped to the
* application-specific logging implementation that supports debug logging.
*
* @note This logging macro is called in the SNTP library with parameters wrapped in
* double parentheses to be ISO C89/C90 standard compliant. For a reference
* POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
* logging-stack in demos folder of the
* [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
*
* <b>Default value</b>: Debug logging is turned off, and no code is generated for calls
* to the macro in the SNTP library on compilation.
*/
#ifndef LogDebug
#define LogDebug( message )
#endif
#endif /* ifndef CORE_SNTP_CONFIG_DEFAULTS_H_ */

View File

@ -0,0 +1,153 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_stubs_stubs.h
* @brief Stubs definitions of UDP transport interface and authentication interface of coreSNTP API.
*/
#ifndef CORE_SNTP_CBMC_STUBS_H_
#define CORE_SNTP_CBMC_STUBS_H_
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "core_sntp_client.h"
/**
* @brief Application defined network interface send function.
*
* @param[in] pNetworkContext Application defined network interface context.
* @param[in] serverAddr Server address to which application sends data.
* @param[in] serverPort Server port to which application sends data.
* @param[out] pBuffer SNTP network send buffer.
* @param[in] bytesToSend Number of bytes to send over the network.
*
* @return Any value from INT32_MIN to INT32_MAX.
*/
int32_t NetworkInterfaceSendStub( NetworkContext_t * pNetworkContext,
uint32_t serverAddr,
uint16_t serverPort,
const void * pBuffer,
uint16_t bytesToSend );
/**
* @brief Application defined network interface receive function.
*
* @param[in] pNetworkContext Application defined network interface context.
* @param[in] serverAddr Server address from which application receives data.
* @param[in] serverPort Server port from which application receives data.
* @param[out] pBuffer SNTP network receive buffer.
* @param[in] bytesToRecv SNTP requested bytes.
*
* @return Any value from INT32_MIN to INT32_MAX.
*/
int32_t NetworkInterfaceReceiveStub( NetworkContext_t * pNetworkContext,
uint32_t serverAddr,
uint16_t serverPort,
void * pBuffer,
uint16_t bytesToRecv );
/**
* @brief Application defined function to generate and append
* authentication code in an SNTP request buffer for the SNTP client to be
* authenticated by the time server, if a security mechanism is used.
*
* @param[in] pContext Application defined authentication interface context.
* @param[in] pTimeServer The time server being used to request time from.
* This parameter is useful to choose the security mechanism when multiple time
* servers are configured in the library, and they require different security
* mechanisms or authentication credentials to use.
* @param[in] pBuffer SNTP request buffer.
* @param[in] bufferSize The maximum amount of data that can be held by the buffer.
* @param[out] pAuthCodeSize This should be filled with size of the authentication
* data appended to the SNTP request buffer, @p pBuffer.
*
* @return The function SHOULD return one of the following integer codes:
* - #SntpSuccess when the authentication data is successfully appended to @p pBuffer.
* - #SntpErrorBufferTooSmall when the user-supplied buffer (to the SntpContext_t through
* @ref Sntp_Init) is not large enough to hold authentication data.
*/
SntpStatus_t GenerateClientAuthStub( SntpAuthContext_t * pContext,
const SntpServerInfo_t * pTimeServer,
void * pBuffer,
size_t bufferSize,
uint16_t * pAuthCodeSize );
/**
* @brief Application defined function to authenticate server by validating
* the authentication code present in its SNTP response to a time request, if
* a security mechanism is supported by the server.
*
* @param[in,out] pContext The application defined NetworkContext_t which
* is opaque to the coreSNTP library.
* @param[in] pTimeServer The time server that has to be authenticated from its
* SNTP response.
* @param[in] pResponseData The SNTP response from the server that contains the
* authentication code after the first #SNTP_PACKET_BASE_SIZE bytes.
* @param[in] responseSize The total size of the response from the server.
*
* @return The function ALWAYS returns #SntpSuccess
*/
SntpStatus_t ValidateServerAuthStub( SntpAuthContext_t * pContext,
const SntpServerInfo_t * pTimeServer,
const void * pResponseData,
uint16_t responseSize );
/**
* @brief Application defined function to resolve time server domain-name
* to an IPv4 address.
*
* @param[in] pTimeServer The time-server whose IPv4 address is to be resolved.
* @param[out] pIpV4Addr This should be filled with the resolved IPv4 address.
* of @p pTimeServer.
*
* @return `true` if DNS resolution is successful; otherwise `false` to represent
* failure.
*/
bool ResolveDnsFuncStub( const SntpServerInfo_t * pServerAddr,
uint32_t * pIpV4Addr );
/**
* @brief Application defined function to obtain the current system time
* in SNTP timestamp format.
*
* @param[out] pCurrentTime This should be filled with the current system time
* in SNTP timestamp format.
*/
void GetTimeFuncStub( SntpTimestamp_t * pCurrentTime );
/**
* @brief Application defined function to update the system clock time
* so that it is synchronized the time server used for getting current time.
*
* @param[in] pTimeServer The time server used to request time.
* @param[in] pServerTime The current time returned by the @p pTimeServer.
* @param[in] clockOffSetMs The calculated clock offset of the system relative
* to the server time.
*/
void SetTimeFuncStub( const SntpServerInfo_t * pTimeServer,
const SntpTimestamp_t * pServerTime,
int64_t clockOffsetMs );
#endif /* ifndef CORE_SNTP_CBMC_STUBS_H_ */

View File

@ -0,0 +1,39 @@
# -*- mode: makefile -*-
# The first line sets the emacs major mode to Makefile
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
################################################################
# Use this file to give project-specific definitions of the command
# line arguments to pass to CBMC tools like goto-cc to build the goto
# binaries and cbmc to do the property and coverage checking.
#
# Use this file to override most default definitions of variables in
# Makefile.common.
################################################################
# Flags to pass to goto-cc for compilation (typically those passed to gcc -c)
COMPILE_FLAGS += -fPIC
COMPILE_FLAGS += -std=gnu90
# Flags to pass to goto-cc for linking (typically those passed to gcc)
# LINK_FLAGS =
# Preprocessor include paths -I...
# Consider adding
# INCLUDES += -I$(CBMC_ROOT)/include
# You will want to decide what order that comes in relative to the other
# include directories in your project.
#
INCLUDES += -I$(SRCDIR)/test/cbmc/include
INCLUDES += -I$(SRCDIR)/source/include
# Preprocessor definitions -D...
# DEFINES =
# Path to arpa executable
# ARPA =
# Flags to pass to cmake for building the project
# ARPA_CMAKE_FLAGS =

View File

@ -0,0 +1,10 @@
# -*- mode: makefile -*-
# The first line sets the emacs major mode to Makefile
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
################################################################
# Use this file to give project-specific targets, including targets
# that may depend on targets defined in Makefile.common.
################################################################

View File

@ -0,0 +1,11 @@
# -*- mode: makefile -*-
# The first line sets the emacs major mode to Makefile
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
################################################################
# Use this file to define project-specific targets and definitions for
# unit testing or continuous integration that may depend on targets
# defined in Makefile.common
################################################################

View File

@ -0,0 +1,20 @@
PROOF_ROOT ?= $(abspath .)
# Absolute path to the root of the source tree.
#
SRCDIR ?= $(abspath $(PROOF_ROOT)/../../..)
# Absolute path to the litani script.
#
LITANI ?= litani
# Name of this proof project, displayed in proof reports. For example,
# "s2n" or "Amazon FreeRTOS". For projects with multiple proof roots,
# this may be overridden on the command-line to Make, for example
#
# make PROJECT_NAME="FreeRTOS MQTT" report
#
PROJECT_NAME = "coreSNTP"

View File

@ -0,0 +1,999 @@
# -*- mode: makefile -*-
# The first line sets the emacs major mode to Makefile
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
CBMC_STARTER_KIT_VERSION = CBMC starter kit 2.5
################################################################
# The CBMC Starter Kit depends on the files Makefile.common and
# run-cbmc-proofs.py. They are installed by the setup script
# cbmc-starter-kit-setup and updated to the latest version by the
# update script cbmc-starter-kit-update. For more information about
# the starter kit and these files and these scripts, see
# https://model-checking.github.io/cbmc-starter-kit
#
# Makefile.common implements what we consider to be some best
# practices for using cbmc for software verification.
#
# Section I gives default values for a large number of Makefile
# variables that control
# * how your code is built (include paths, etc),
# * what program transformations are applied to your code (loop
# unwinding, etc), and
# * what properties cbmc checks for in your code (memory safety, etc).
#
# These variables are defined below with definitions of the form
# VARIABLE ?= DEFAULT_VALUE
# meaning VARIABLE is set to DEFAULT_VALUE if VARIABLE has not already
# been given a value.
#
# For your project, you can override these default values with
# project-specific definitions in Makefile-project-defines.
#
# For any individual proof, you can override these default values and
# project-specific values with proof-specific definitions in the
# Makefile for your proof.
#
# The definitions in the proof Makefile override definitions in the
# project Makefile-project-defines which override definitions in this
# Makefile.common.
#
# Section II uses the values defined in Section I to build your code, run
# your proof, and build a report of your results. You should not need
# to modify or override anything in Section II, but you may want to
# read it to understand how the values defined in Section I control
# things.
#
# To use Makefile.common, set variables as described above as needed,
# and then for each proof,
#
# * Create a subdirectory <DIR>.
# * Write a proof harness (a function) with the name <HARNESS_ENTRY>
# in a file with the name <DIR>/<HARNESS_FILE>.c
# * Write a makefile with the name <DIR>/Makefile that looks
# something like
#
# HARNESS_FILE=<HARNESS_FILE>
# HARNESS_ENTRY=<HARNESS_ENTRY>
# PROOF_UID=<PROOF_UID>
#
# PROJECT_SOURCES += $(SRCDIR)/libraries/api_1.c
# PROJECT_SOURCES += $(SRCDIR)/libraries/api_2.c
#
# PROOF_SOURCES += $(PROOFDIR)/harness.c
# PROOF_SOURCES += $(SRCDIR)/cbmc/proofs/stub_a.c
# PROOF_SOURCES += $(SRCDIR)/cbmc/proofs/stub_b.c
#
# UNWINDSET += foo.0:3
# UNWINDSET += bar.1:6
#
# REMOVE_FUNCTION_BODY += api_stub_a
# REMOVE_FUNCTION_BODY += api_stub_b
#
# DEFINES = -DDEBUG=0
#
# include ../Makefile.common
#
# * Change directory to <DIR> and run make
#
# The proof setup script cbmc-starter-kit-setup-proof from the CBMC
# Starter Kit will do most of this for, creating a directory and
# writing a basic Makefile and proof harness into it that you can edit
# as described above.
#
# Warning: If you get results that are hard to explain, consider
# running "make clean" or "make veryclean" before "make" if you get
# results that are hard to explain. Dependency handling in this
# Makefile.common may not be perfect.
SHELL=/bin/bash
default: report
################################################################
################################################################
## Section I: This section gives common variable definitions.
##
## Override these definitions in Makefile-project-defines or
## your proof Makefile.
##
## Remember that Makefile.common and Makefile-project-defines are
## included into the proof Makefile in your proof directory, so all
## relative pathnames defined there should be relative to your proof
## directory.
################################################################
# Define the layout of the source tree and the proof subtree
#
# Generally speaking,
#
# SRCDIR = the root of the repository
# CBMC_ROOT = /srcdir/cbmc
# PROOF_ROOT = /srcdir/cbmc/proofs
# PROOF_SOURCE = /srcdir/cbmc/sources
# PROOF_INCLUDE = /srcdir/cbmc/include
# PROOF_STUB = /srcdir/cbmc/stubs
# PROOFDIR = the directory containing the Makefile for your proof
#
# The path /srcdir/cbmc used in the example above is determined by the
# setup script cbmc-starter-kit-setup. Projects usually create a cbmc
# directory somewhere in the source tree, and run the setup script in
# that directory. The value of CBMC_ROOT becomes the absolute path to
# that directory.
#
# The location of that cbmc directory in the source tree affects the
# definition of SRCDIR, which is defined in terms of the relative path
# from a proof directory to the repository root. The definition is
# usually determined by the setup script cbmc-starter-kit-setup and
# written to Makefile-template-defines, but you can override it for a
# project in Makefile-project-defines and for a specific proof in the
# Makefile for the proof.
# Absolute path to the directory containing this Makefile.common
# See https://ftp.gnu.org/old-gnu/Manuals/make-3.80/html_node/make_17.html
#
# Note: We compute the absolute paths to the makefiles in MAKEFILE_LIST
# before we filter the list of makefiles for %/Makefile.common.
# Otherwise an invocation of the form "make -f Makefile.common" will set
# MAKEFILE_LIST to "Makefile.common" which will fail to match the
# pattern %/Makefile.common.
#
MAKEFILE_PATHS = $(foreach makefile,$(MAKEFILE_LIST),$(abspath $(makefile)))
PROOF_ROOT = $(dir $(filter %/Makefile.common,$(MAKEFILE_PATHS)))
CBMC_ROOT = $(shell dirname $(PROOF_ROOT))
PROOF_SOURCE = $(CBMC_ROOT)/sources
PROOF_INCLUDE = $(CBMC_ROOT)/include
PROOF_STUB = $(CBMC_ROOT)/stubs
# Project-specific definitions to override default definitions below
# * Makefile-project-defines will never be overwritten
# * Makefile-template-defines may be overwritten when the starter
# kit is updated
sinclude $(PROOF_ROOT)/Makefile-project-defines
sinclude $(PROOF_ROOT)/Makefile-template-defines
# SRCDIR is the path to the root of the source tree
# This is a default definition that is frequently overridden in
# another Makefile, see the discussion of SRCDIR above.
SRCDIR ?= $(abspath ../..)
# PROOFDIR is the path to the directory containing the proof harness
PROOFDIR ?= $(abspath .)
################################################################
# Define how to run CBMC
# Do property checking with the external SAT solver given by
# EXTERNAL_SAT_SOLVER. Do coverage checking with the default solver,
# since coverage checking requires the use of an incremental solver.
# The EXTERNAL_SAT_SOLVER variable is typically set (if it is at all)
# as an environment variable or as a makefile variable in
# Makefile-project-defines.
#
# For a particular proof, if the default solver is faster, do property
# checking with the default solver by including this definition in the
# proof Makefile:
# USE_EXTERNAL_SAT_SOLVER =
#
ifneq ($(strip $(EXTERNAL_SAT_SOLVER)),)
USE_EXTERNAL_SAT_SOLVER ?= --external-sat-solver $(EXTERNAL_SAT_SOLVER)
endif
CHECKFLAGS += $(USE_EXTERNAL_SAT_SOLVER)
# Job pools
# For version of Litani that are new enough (where `litani print-capabilities`
# prints "pools"), proofs for which `EXPENSIVE = true` is set can be added to a
# "job pool" that restricts how many expensive proofs are run at a time. All
# other proofs will be built in parallel as usual.
#
# In more detail: all compilation, instrumentation, and report jobs are run with
# full parallelism as usual, even for expensive proofs. The CBMC jobs for
# non-expensive proofs are also run in parallel. The only difference is that the
# CBMC safety checks and coverage checks for expensive proofs are run with a
# restricted parallelism level. At any one time, only N of these jobs are run at
# once, amongst all the proofs.
#
# To configure N, Litani needs to be initialized with a pool called "expensive".
# For example, to only run two CBMC safety/coverage jobs at a time from amongst
# all the proofs, you would initialize litani like
# litani init --pools expensive:2
# The run-cbmc-proofs.py script takes care of this initialization through the
# --expensive-jobs-parallelism flag.
#
# To enable this feature, set
# the ENABLE_POOLS variable when running Make, like
# `make ENABLE_POOLS=true report`
# The run-cbmc-proofs.py script takes care of this through the
# --restrict-expensive-jobs flag.
ifeq ($(strip $(ENABLE_POOLS)),)
POOL =
else ifeq ($(strip $(EXPENSIVE)),)
POOL =
else
POOL = --pool expensive
endif
# Similar to the pool feature above. If Litani is new enough, enable
# profiling CBMC's memory use.
ifeq ($(strip $(ENABLE_MEMORY_PROFILING)),)
MEMORY_PROFILING =
else
MEMORY_PROFILING = --profile-memory
endif
# Property checking flags
#
# Each variable below controls a specific property checking flag
# within CBMC. If desired, a property flag can be disabled within
# a particular proof by nulling the corresponding variable. For
# instance, the following line:
#
# CHECK_FLAG_POINTER_CHECK =
#
# would disable the --pointer-check CBMC flag within:
# * an entire project when added to Makefile-project-defines
# * a specific proof when added to the harness Makefile
CBMC_FLAG_MALLOC_MAY_FAIL ?= --malloc-may-fail
CBMC_FLAG_MALLOC_FAIL_NULL ?= --malloc-fail-null
CBMC_FLAG_BOUNDS_CHECK ?= --bounds-check
CBMC_FLAG_CONVERSION_CHECK ?= --conversion-check
CBMC_FLAG_DIV_BY_ZERO_CHECK ?= --div-by-zero-check
CBMC_FLAG_FLOAT_OVERFLOW_CHECK ?= --float-overflow-check
CBMC_FLAG_NAN_CHECK ?= --nan-check
CBMC_FLAG_POINTER_CHECK ?= --pointer-check
CBMC_FLAG_POINTER_OVERFLOW_CHECK ?= --pointer-overflow-check
CBMC_FLAG_POINTER_PRIMITIVE_CHECK ?= --pointer-primitive-check
CBMC_FLAG_SIGNED_OVERFLOW_CHECK ?= --signed-overflow-check
CBMC_FLAG_UNDEFINED_SHIFT_CHECK ?= --undefined-shift-check
CBMC_FLAG_UNSIGNED_OVERFLOW_CHECK ?= --unsigned-overflow-check
CBMC_FLAG_UNWINDING_ASSERTIONS ?= --unwinding-assertions
CBMC_FLAG_UNWIND ?= --unwind 1
CBMC_FLAG_FLUSH ?= --flush
# CBMC flags used for property checking and coverage checking
CBMCFLAGS += $(CBMC_FLAG_UNWIND) $(CBMC_UNWINDSET) $(CBMC_FLAG_FLUSH)
# CBMC flags used for property checking
CHECKFLAGS += $(CBMC_FLAG_MALLOC_MAY_FAIL)
CHECKFLAGS += $(CBMC_FLAG_MALLOC_FAIL_NULL)
CHECKFLAGS += $(CBMC_FLAG_BOUNDS_CHECK)
CHECKFLAGS += $(CBMC_FLAG_CONVERSION_CHECK)
CHECKFLAGS += $(CBMC_FLAG_DIV_BY_ZERO_CHECK)
CHECKFLAGS += $(CBMC_FLAG_FLOAT_OVERFLOW_CHECK)
CHECKFLAGS += $(CBMC_FLAG_NAN_CHECK)
CHECKFLAGS += $(CBMC_FLAG_POINTER_CHECK)
CHECKFLAGS += $(CBMC_FLAG_POINTER_OVERFLOW_CHECK)
CHECKFLAGS += $(CBMC_FLAG_POINTER_PRIMITIVE_CHECK)
CHECKFLAGS += $(CBMC_FLAG_SIGNED_OVERFLOW_CHECK)
CHECKFLAGS += $(CBMC_FLAG_UNDEFINED_SHIFT_CHECK)
CHECKFLAGS += $(CBMC_FLAG_UNSIGNED_OVERFLOW_CHECK)
CHECKFLAGS += $(CBMC_FLAG_UNWINDING_ASSERTIONS)
# CBMC flags used for coverage checking
COVERFLAGS += $(CBMC_FLAG_MALLOC_MAY_FAIL)
COVERFLAGS += $(CBMC_FLAG_MALLOC_FAIL_NULL)
# Additional CBMC flag to CBMC control verbosity.
#
# Meaningful values are
# 0 none
# 1 only errors
# 2 + warnings
# 4 + results
# 6 + status/phase information
# 8 + statistical information
# 9 + progress information
# 10 + debug info
#
# Uncomment the following line or set in Makefile-project-defines
# CBMC_VERBOSITY ?= --verbosity 4
# Additional CBMC flag to control how CBMC treats static variables.
#
# NONDET_STATIC is a list of flags of the form --nondet-static
# and --nondet-static-exclude VAR. The --nondet-static flag causes
# CBMC to initialize static variables with unconstrained value
# (ignoring initializers and default zero-initialization). The
# --nondet-static-exclude VAR excludes VAR for the variables
# initialized with unconstrained values.
NONDET_STATIC ?=
# Flags to pass to goto-cc for compilation and linking
COMPILE_FLAGS ?= -Wall
LINK_FLAGS ?= -Wall
EXPORT_FILE_LOCAL_SYMBOLS ?= --export-file-local-symbols
# Preprocessor include paths -I...
INCLUDES ?=
# Preprocessor definitions -D...
DEFINES ?=
# CBMC object model
#
# CBMC_OBJECT_BITS is the number of bits in a pointer CBMC uses for
# the id of the object to which a pointer is pointing. CBMC uses 8
# bits for the object id by default. The remaining bits in the pointer
# are used for offset into the object. This limits the size of the
# objects that CBMC can model. This Makefile defines this bound on
# object size to be CBMC_MAX_OBJECT_SIZE. You are likely to get
# unexpected results if you try to malloc an object larger than this
# bound.
CBMC_OBJECT_BITS ?= 8
# CBMC loop unwinding (Normally set in the proof Makefile)
#
# UNWINDSET is a list of pairs of the form foo.1:4 meaning that
# CBMC should unwind loop 1 in function foo no more than 4 times.
# For historical reasons, the number 4 is one more than the number
# of times CBMC actually unwinds the loop.
UNWINDSET ?=
# CBMC early loop unwinding (Normally set in the proof Makefile)
#
# Most users can ignore this variable.
#
# This variable exists to support the use of loop and function
# contracts, two features under development for CBMC. Checking the
# assigns clause for function contracts and loop invariants currently
# assumes loop-free bodies for loops and functions with contracts
# (possibly after replacing nested loops with their own loop
# contracts). To satisfy this requirement, it may be necessary to
# unwind some loops before the function contract and loop invariant
# transformations are applied to the goto program. This variable
# EARLY_UNWINDSET is identical to UNWINDSET, and we assume that the
# loops mentioned in EARLY_UNWINDSET and UNWINDSET are disjoint.
EARLY_UNWINDSET ?=
# CBMC function removal (Normally set set in the proof Makefile)
#
# REMOVE_FUNCTION_BODY is a list of function names. CBMC will "undefine"
# the function, and CBMC will treat the function as having no side effects
# and returning an unconstrained value of the appropriate return type.
# The list should include the names of functions being stubbed out.
REMOVE_FUNCTION_BODY ?=
# CBMC function pointer restriction (Normally set in the proof Makefile)
#
# RESTRICT_FUNCTION_POINTER is a list of function pointer restriction
# instructions of the form:
#
# <fun_id>.function_pointer_call.<N>/<fun_id>[,<fun_id>]*
#
# The function pointer call number <N> in the specified function gets
# rewritten to a case switch over a finite list of functions.
# If some possible target functions are omitted from the list a counter
# example trace will be found by CBMC, i.e. the transformation is sound.
# If the target functions are file-local symbols, then mangled names must
# be used.
RESTRICT_FUNCTION_POINTER ?=
# The project source files (Normally set set in the proof Makefile)
#
# PROJECT_SOURCES is the list of project source files to compile,
# including the source file defining the function under test.
PROJECT_SOURCES ?=
# The proof source files (Normally set in the proof Makefile)
#
# PROOF_SOURCES is the list of proof source files to compile, including
# the proof harness, and including any function stubs being used.
PROOF_SOURCES ?=
# The number of seconds that CBMC should be allowed to run for before
# being forcefully terminated. Currently, this is set to be less than
# the time limit for a CodeBuild job, which is eight hours. If a proof
# run takes longer than the time limit of the CI environment, the
# environment will halt the proof run without updating the Litani
# report, making the proof run appear to "hang".
CBMC_TIMEOUT ?= 21600
# Proof writers could add function contracts in their source code.
# These contracts are ignored by default, but may be enabled in two distinct
# contexts using the following two variables:
# 1. To check whether one or more function contracts are sound with respect to
# the function implementation, CHECK_FUNCTION_CONTRACTS should be a list of
# function names.
# 2. To replace calls to certain functions with their correspondent function
# contracts, USE_FUNCTION_CONTRACTS should be a list of function names.
# One must check separately whether a function contract is sound before
# replacing it in calling contexts.
CHECK_FUNCTION_CONTRACTS ?=
CBMC_CHECK_FUNCTION_CONTRACTS := $(patsubst %,--enforce-contract %, $(CHECK_FUNCTION_CONTRACTS))
USE_FUNCTION_CONTRACTS ?=
CBMC_USE_FUNCTION_CONTRACTS := $(patsubst %,--replace-call-with-contract %, $(USE_FUNCTION_CONTRACTS))
# Similarly, proof writers could also add loop contracts in their source code
# to obtain unbounded correctness proofs. Unlike function contracts, loop
# contracts are not reusable and thus are checked and used simultaneously.
# These contracts are also ignored by default, but may be enabled by setting
# the APPLY_LOOP_CONTRACTS variable to 1.
APPLY_LOOP_CONTRACTS ?= 0
ifeq ($(APPLY_LOOP_CONTRACTS),1)
CBMC_APPLY_LOOP_CONTRACTS ?= --apply-loop-contracts
endif
# Silence makefile output (eg, long litani commands) unless VERBOSE is set.
ifndef VERBOSE
MAKEFLAGS := $(MAKEFLAGS) -s
endif
################################################################
################################################################
## Section II: This section defines the process of running a proof
##
## There should be no reason to edit anything below this line.
################################################################
# Paths
CBMC ?= cbmc
GOTO_ANALYZER ?= goto-analyzer
GOTO_CC ?= goto-cc
GOTO_INSTRUMENT ?= goto-instrument
CRANGLER ?= crangler
VIEWER ?= cbmc-viewer
MAKE_SOURCE ?= make-source
VIEWER2 ?= cbmc-viewer
CMAKE ?= cmake
GOTODIR ?= $(PROOFDIR)/gotos
LOGDIR ?= $(PROOFDIR)/logs
PROJECT ?= project
PROOF ?= proof
HARNESS_GOTO ?= $(GOTODIR)/$(HARNESS_FILE)
PROJECT_GOTO ?= $(GOTODIR)/$(PROJECT)
PROOF_GOTO ?= $(GOTODIR)/$(PROOF)
################################################################
# Useful macros for values that are hard to reference
SPACE :=$() $()
COMMA :=,
################################################################
# Set C compiler defines
CBMCFLAGS += --object-bits $(CBMC_OBJECT_BITS)
COMPILE_FLAGS += --object-bits $(CBMC_OBJECT_BITS)
DEFINES += -DCBMC=1
DEFINES += -DCBMC_OBJECT_BITS=$(CBMC_OBJECT_BITS)
DEFINES += -DCBMC_MAX_OBJECT_SIZE="(SIZE_MAX>>(CBMC_OBJECT_BITS+1))"
# CI currently assumes cbmc invocation has at most one --unwindset
ifdef UNWINDSET
ifneq ($(strip $(UNWINDSET)),"")
CBMC_UNWINDSET := --unwindset $(subst $(SPACE),$(COMMA),$(strip $(UNWINDSET)))
endif
endif
ifdef EARLY_UNWINDSET
ifneq ($(strip $(EARLY_UNWINDSET)),"")
CBMC_EARLY_UNWINDSET := --unwindset $(subst $(SPACE),$(COMMA),$(strip $(EARLY_UNWINDSET)))
endif
endif
CBMC_REMOVE_FUNCTION_BODY := $(patsubst %,--remove-function-body %, $(REMOVE_FUNCTION_BODY))
CBMC_RESTRICT_FUNCTION_POINTER := $(patsubst %,--restrict-function-pointer %, $(RESTRICT_FUNCTION_POINTER))
################################################################
# Targets for rewriting source files with crangler
# Construct crangler configuration files
#
# REWRITTEN_SOURCES is a list of crangler output files source.i.
# This target assumes that for each source.i
# * source.i_SOURCE is the path to a source file,
# * source.i_FUNCTIONS is a list of functions (may be empty)
# * source.i_OBJECTS is a list of variables (may be empty)
# This target constructs the crangler configuration file source.i.json
# of the form
# {
# "sources": [ "/proj/code.c" ],
# "includes": [ "/proj/include" ],
# "defines": [ "VAR=1" ],
# "functions": [ {"function_name": ["remove static"]} ],
# "objects": [ {"variable_name": ["remove static"]} ],
# "output": "source.i"
# }
# to remove the static attribute from function_name and variable_name
# in the source file source.c and write the result to source.i.
#
# This target assumes that filenames include no spaces and that
# the INCLUDES and DEFINES variables include no spaces after -I
# and -D. For example, use "-DVAR=1" and not "-D VAR=1".
#
# Define *_SOURCE, *_FUNCTIONS, and *_OBJECTS in the proof Makefile.
# The string source.i is usually an absolute path $(PROOFDIR)/code.i
# to a file in the proof directory that contains the proof Makefile.
# The proof Makefile usually includes the definitions
# $(PROOFDIR)/code.i_SOURCE = /proj/code.c
# $(PROOFDIR)/code.i_FUNCTIONS = function_name
# $(PROOFDIR)/code.i_OBJECTS = variable_name
# Because these definitions refer to PROOFDIR that is defined in this
# Makefile.common, these definitions must appear after the inclusion
# of Makefile.common in the proof Makefile.
#
$(foreach rs,$(REWRITTEN_SOURCES),$(eval $(rs).json: $($(rs)_SOURCE)))
$(foreach rs,$(REWRITTEN_SOURCES),$(rs).json):
echo '{'\
'"sources": ['\
'"$($(@:.json=)_SOURCE)"'\
'],'\
'"includes": ['\
'$(subst $(SPACE),$(COMMA),$(patsubst -I%,"%",$(strip $(INCLUDES))))' \
'],'\
'"defines": ['\
'$(subst $(SPACE),$(COMMA),$(patsubst -D%,"%",$(subst ",\",$(strip $(DEFINES)))))' \
'],'\
'"functions": ['\
'{'\
'$(subst ~, ,$(subst $(SPACE),$(COMMA),$(patsubst %,"%":["remove~static"],$($(@:.json=)_FUNCTIONS))))' \
'}'\
'],'\
'"objects": ['\
'{'\
'$(subst ~, ,$(subst $(SPACE),$(COMMA),$(patsubst %,"%":["remove~static"],$($(@:.json=)_OBJECTS))))' \
'}'\
'],'\
'"output": "$(@:.json=)"'\
'}' > $@
# Rewrite source files with crangler
#
$(foreach rs,$(REWRITTEN_SOURCES),$(eval $(rs): $(rs).json))
$(REWRITTEN_SOURCES):
$(LITANI) add-job \
--command \
'$(CRANGLER) $@.json' \
--inputs $($@_SOURCE) \
--outputs $@ \
--stdout-file $(LOGDIR)/crangler-$(subst /,_,$(subst .,_,$@))-log.txt \
--interleave-stdout-stderr \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): removing static"
################################################################
# Build targets that make the relevant .goto files
# Compile project sources
$(PROJECT_GOTO)1.goto: $(PROJECT_SOURCES) $(REWRITTEN_SOURCES)
$(LITANI) add-job \
--command \
'$(GOTO_CC) $(CBMC_VERBOSITY) $(COMPILE_FLAGS) $(EXPORT_FILE_LOCAL_SYMBOLS) $(INCLUDES) $(DEFINES) $^ -o $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/project_sources-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): building project binary"
# Compile proof sources
$(PROOF_GOTO)1.goto: $(PROOF_SOURCES)
$(LITANI) add-job \
--command \
'$(GOTO_CC) $(CBMC_VERBOSITY) $(COMPILE_FLAGS) $(EXPORT_FILE_LOCAL_SYMBOLS) $(INCLUDES) $(DEFINES) $^ -o $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/proof_sources-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): building proof binary"
# Remove function bodies from project sources
$(PROJECT_GOTO)2.goto: $(PROJECT_GOTO)1.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_REMOVE_FUNCTION_BODY) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/remove_function_body-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): removing function bodies from project sources"
# Link project and proof sources into the proof harness
$(HARNESS_GOTO)1.goto: $(PROOF_GOTO)1.goto $(PROJECT_GOTO)2.goto
$(LITANI) add-job \
--command '$(GOTO_CC) $(CBMC_VERBOSITY) --function $(HARNESS_ENTRY) $^ $(LINK_FLAGS) -o $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/link_proof_project-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): linking project to proof"
# Restrict function pointers
$(HARNESS_GOTO)2.goto: $(HARNESS_GOTO)1.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_RESTRICT_FUNCTION_POINTER) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/restrict_function_pointer-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): restricting function pointers in project sources"
# Fill static variable with unconstrained values
$(HARNESS_GOTO)3.goto: $(HARNESS_GOTO)2.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(NONDET_STATIC) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/nondet_static-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): setting static variables to nondet"
# Omit unused functions (sharpens coverage calculations)
$(HARNESS_GOTO)4.goto: $(HARNESS_GOTO)3.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) --drop-unused-functions $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/drop_unused_functions-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): dropping unused functions"
# Omit initialization of unused global variables (reduces problem size)
$(HARNESS_GOTO)5.goto: $(HARNESS_GOTO)4.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) --slice-global-inits $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/slice_global_inits-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): slicing global initializations"
# Replace function calls with function contracts
# This must be done before enforcing function contracts,
# since contract enforcement inlines all function calls.
$(HARNESS_GOTO)6.goto: $(HARNESS_GOTO)5.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_USE_FUNCTION_CONTRACTS) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/use_function_contracts-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): replacing function calls with function contracts"
# Unwind loops for loop and function contracts
$(HARNESS_GOTO)7.goto: $(HARNESS_GOTO)6.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_EARLY_UNWINDSET) $(CBMC_FLAG_UNWINDING_ASSERTIONS) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/unwind_loops-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): unwinding loops"
# Apply loop contracts
$(HARNESS_GOTO)8.goto: $(HARNESS_GOTO)7.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_APPLY_LOOP_CONTRACTS) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/apply_loop_contracts-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): applying loop contracts"
# Check function contracts
$(HARNESS_GOTO)9.goto: $(HARNESS_GOTO)8.goto
$(LITANI) add-job \
--command \
'$(GOTO_INSTRUMENT) $(CBMC_VERBOSITY) $(CBMC_CHECK_FUNCTION_CONTRACTS) $^ $@' \
--inputs $^ \
--outputs $@ \
--stdout-file $(LOGDIR)/check_function_contracts-log.txt \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): checking function contracts"
# Final name for proof harness
$(HARNESS_GOTO).goto: $(HARNESS_GOTO)9.goto
$(LITANI) add-job \
--command 'cp $< $@' \
--inputs $^ \
--outputs $@ \
--pipeline-name "$(PROOF_UID)" \
--ci-stage build \
--description "$(PROOF_UID): copying final goto-binary"
################################################################
# Targets to run the analysis commands
$(LOGDIR)/result.txt: $(HARNESS_GOTO).goto
$(LITANI) add-job \
$(POOL) \
--command \
'$(CBMC) $(CBMC_VERBOSITY) $(CBMCFLAGS) $(CBMC_FLAG_UNWINDING_ASSERTIONS) $(CHECKFLAGS) --trace $<' \
--inputs $^ \
--outputs $@ \
--ci-stage test \
--stdout-file $@ \
$(MEMORY_PROFILING) \
--ignore-returns 10 \
--timeout $(CBMC_TIMEOUT) \
--pipeline-name "$(PROOF_UID)" \
--tags "stats-group:safety checks" \
--stderr-file $(LOGDIR)/result-err-log.txt \
--description "$(PROOF_UID): checking safety properties"
$(LOGDIR)/result.xml: $(HARNESS_GOTO).goto
$(LITANI) add-job \
$(POOL) \
--command \
'$(CBMC) $(CBMC_VERBOSITY) $(CBMCFLAGS) $(CBMC_FLAG_UNWINDING_ASSERTIONS) $(CHECKFLAGS) --trace --xml-ui $<' \
--inputs $^ \
--outputs $@ \
--ci-stage test \
--stdout-file $@ \
$(MEMORY_PROFILING) \
--ignore-returns 10 \
--timeout $(CBMC_TIMEOUT) \
--pipeline-name "$(PROOF_UID)" \
--tags "stats-group:safety checks" \
--stderr-file $(LOGDIR)/result-err-log.txt \
--description "$(PROOF_UID): checking safety properties"
$(LOGDIR)/property.xml: $(HARNESS_GOTO).goto
$(LITANI) add-job \
--command \
'$(CBMC) $(CBMC_VERBOSITY) $(CBMCFLAGS) $(CBMC_FLAG_UNWINDING_ASSERTIONS) $(CHECKFLAGS) --show-properties --xml-ui $<' \
--inputs $^ \
--outputs $@ \
--ci-stage test \
--stdout-file $@ \
--ignore-returns 10 \
--pipeline-name "$(PROOF_UID)" \
--stderr-file $(LOGDIR)/property-err-log.txt \
--description "$(PROOF_UID): printing safety properties"
$(LOGDIR)/coverage.xml: $(HARNESS_GOTO).goto
$(LITANI) add-job \
$(POOL) \
--command \
'$(CBMC) $(CBMC_VERBOSITY) $(CBMCFLAGS) $(COVERFLAGS) --cover location --xml-ui $<' \
--inputs $^ \
--outputs $@ \
--ci-stage test \
--stdout-file $@ \
$(MEMORY_PROFILING) \
--ignore-returns 10 \
--timeout $(CBMC_TIMEOUT) \
--pipeline-name "$(PROOF_UID)" \
--tags "stats-group:coverage computation" \
--stderr-file $(LOGDIR)/coverage-err-log.txt \
--description "$(PROOF_UID): calculating coverage"
define VIEWER_CMD
$(VIEWER) \
--result $(LOGDIR)/result.txt \
--block $(LOGDIR)/coverage.xml \
--property $(LOGDIR)/property.xml \
--srcdir $(SRCDIR) \
--goto $(HARNESS_GOTO).goto \
--htmldir $(PROOFDIR)/html
endef
export VIEWER_CMD
$(PROOFDIR)/html: $(LOGDIR)/result.txt $(LOGDIR)/property.xml $(LOGDIR)/coverage.xml
$(LITANI) add-job \
--command "$$VIEWER_CMD" \
--inputs $^ \
--outputs $(PROOFDIR)/html \
--pipeline-name "$(PROOF_UID)" \
--ci-stage report \
--stdout-file $(LOGDIR)/viewer-log.txt \
--description "$(PROOF_UID): generating report"
# Caution: run make-source before running property and coverage checking
# The current make-source script removes the goto binary
$(LOGDIR)/source.json:
mkdir -p $(dir $@)
$(RM) -r $(GOTODIR)
$(MAKE_SOURCE) --srcdir $(SRCDIR) --wkdir $(PROOFDIR) > $@
$(RM) -r $(GOTODIR)
define VIEWER2_CMD
$(VIEWER2) \
--result $(LOGDIR)/result.xml \
--coverage $(LOGDIR)/coverage.xml \
--property $(LOGDIR)/property.xml \
--srcdir $(SRCDIR) \
--goto $(HARNESS_GOTO).goto \
--reportdir $(PROOFDIR)/report \
--config $(PROOFDIR)/cbmc-viewer.json
endef
export VIEWER2_CMD
# Omit logs/source.json from report generation until make-sources
# works correctly with Makefiles that invoke the compiler with
# mutliple source files at once.
$(PROOFDIR)/report: $(LOGDIR)/result.xml $(LOGDIR)/property.xml $(LOGDIR)/coverage.xml
$(LITANI) add-job \
--command "$$VIEWER2_CMD" \
--inputs $^ \
--outputs $(PROOFDIR)/report \
--pipeline-name "$(PROOF_UID)" \
--stdout-file $(LOGDIR)/viewer-log.txt \
--ci-stage report \
--description "$(PROOF_UID): generating report"
litani-path:
@echo $(LITANI)
# ##############################################################
# Phony Rules
#
# These rules provide a convenient way to run a single proof up to a
# certain stage. Users can browse into a proof directory and run
# "make -Bj 3 report" to generate a report for just that proof, or
# "make goto" to build the goto binary. Under the hood, this runs litani
# for just that proof.
_goto: $(HARNESS_GOTO).goto
goto:
@ echo Running 'litani init'
$(LITANI) init --project $(PROJECT_NAME)
@ echo Running 'litani add-job'
$(MAKE) -B _goto
@ echo Running 'litani build'
$(LITANI) run-build
_result: $(LOGDIR)/result.txt
result:
@ echo Running 'litani init'
$(LITANI) init --project $(PROJECT_NAME)
@ echo Running 'litani add-job'
$(MAKE) -B _result
@ echo Running 'litani build'
$(LITANI) run-build
_property: $(LOGDIR)/property.xml
property:
@ echo Running 'litani init'
$(LITANI) init --project $(PROJECT_NAME)
@ echo Running 'litani add-job'
$(MAKE) -B _property
@ echo Running 'litani build'
$(LITANI) run-build
_coverage: $(LOGDIR)/coverage.xml
coverage:
@ echo Running 'litani init'
$(LITANI) init --project $(PROJECT_NAME)
@ echo Running 'litani add-job'
$(MAKE) -B _coverage
@ echo Running 'litani build'
$(LITANI) run-build
# Choose the invocation of cbmc-viewer depending on which version of
# cbmc-viewer is installed. The --version flag is not implemented in
# version 1 --- it is an "unrecognized argument" --- but it is
# implemented in version 2.
_report1: $(PROOFDIR)/html
_report2: $(PROOFDIR)/report
_report:
(cbmc-viewer --version 2>&1 | grep "unrecognized argument" > /dev/null) && \
$(MAKE) -B _report1 || $(MAKE) -B _report2
report report1 report2:
@ echo Running 'litani init'
$(LITANI) init --project $(PROJECT_NAME)
@ echo Running 'litani add-job'
$(MAKE) -B _report
@ echo Running 'litani build'
$(LITANI) run-build
################################################################
# Targets to clean up after ourselves
clean:
-$(RM) $(DEPENDENT_GOTOS)
-$(RM) TAGS*
-$(RM) *~ \#*
-$(RM) $(REWRITTEN_SOURCES) $(foreach rs,$(REWRITTEN_SOURCES),$(rs).json)
veryclean: clean
-$(RM) -r html report
-$(RM) -r $(LOGDIR) $(GOTODIR)
.PHONY: \
_coverage \
_goto \
_property \
_report \
_report2 \
_result \
clean \
coverage \
goto \
litani-path \
property \
report \
report2 \
result \
setup_dependencies \
testdeps \
veryclean \
#
################################################################
# Rule for generating cbmc-batch.yaml, used by the CI at
# https://github.com/awslabs/aws-batch-cbmc/
JOB_OS ?= ubuntu16
JOB_MEMORY ?= 32000
# Proofs that are expected to fail should set EXPECTED to
# "FAILED" in their Makefile. Values other than SUCCESSFUL
# or FAILED will cause a CI error.
EXPECTED ?= SUCCESSFUL
define yaml_encode_options
"$(shell echo $(1) | sed 's/ ,/ /g' | sed 's/ /;/g')"
endef
CI_FLAGS = $(CBMCFLAGS) $(CHECKFLAGS) $(COVERFLAGS)
cbmc-batch.yaml:
@$(RM) $@
@echo 'build_memory: $(JOB_MEMORY)' > $@
@echo 'cbmcflags: $(strip $(call yaml_encode_options,$(CI_FLAGS)))' >> $@
@echo 'coverage_memory: $(JOB_MEMORY)' >> $@
@echo 'expected: $(EXPECTED)' >> $@
@echo 'goto: $(HARNESS_GOTO).goto' >> $@
@echo 'jobos: $(JOB_OS)' >> $@
@echo 'property_memory: $(JOB_MEMORY)' >> $@
@echo 'report_memory: $(JOB_MEMORY)' >> $@
.PHONY: cbmc-batch.yaml
################################################################
# Run "make echo-proof-uid" to print the proof ID of a proof. This can be
# used by scripts to ensure that every proof has an ID, that there are
# no duplicates, etc.
.PHONY: echo-proof-uid
echo-proof-uid:
@echo $(PROOF_UID)
.PHONY: echo-project-name
echo-project-name:
@echo $(PROJECT_NAME)
################################################################
# Project-specific targets requiring values defined above
sinclude $(PROOF_ROOT)/Makefile-project-targets
# CI-specific targets to drive cbmc in CI
sinclude $(PROOF_ROOT)/Makefile-project-testing
################################################################

View File

@ -0,0 +1,27 @@
CBMC proofs
===========
This directory contains the CBMC proofs. Each proof is in its own
directory.
This directory includes four Makefiles.
One Makefile describes the basic workflow for building and running proofs:
* Makefile.common:
* make: builds the goto binary, does the cbmc property checking
and coverage checking, and builds the final report.
* make goto: builds the goto binary
* make result: does cbmc property checking
* make coverage: does cbmc coverage checking
* make report: builds the final report
Three included Makefiles describe project-specific settings and can override
definitions in Makefile.common:
* Makefile-project-defines: definitions like compiler flags
required to build the goto binaries, and definitions to override
definitions in Makefile.common.
* Makefile-project-targets: other make targets needed for the project
* Makefile-project-testing: other definitions and targets needed for
unit testing or continuous integration.

View File

@ -0,0 +1,24 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
HARNESS_ENTRY = harness
HARNESS_FILE = Sntp_CalculatePollInterval_harness
# This should be a unique identifier for this proof, and will appear on the
# Litani dashboard. It can be human-readable and contain spaces if you wish.
PROOF_UID = Sntp_CalculatePollInterval
# Bound for loop unwinding for loop in Sntp_CalculatePollInterval function. Unwinding
# the loop 33 times will be enough as the unsigned 32 integer can take value upto 2^32.
MAX_BOUND_FOR_LOOP=33
DEFINES +=
INCLUDES +=
REMOVE_FUNCTION_BODY +=
UNWINDSET += Sntp_CalculatePollInterval.0:$(MAX_BOUND_FOR_LOOP)
PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
PROJECT_SOURCES += $(SRCDIR)/source/core_sntp_serializer.c
include ../Makefile.common

View File

@ -0,0 +1,23 @@
Sntp_CalculatePollInterval proof
==============
This directory contains a memory safety proof for Sntp_CalculatePollInterval.
The proof runs within 3 minutes on a t2.2xlarge. It provides complete coverage of:
* Sntp_CalculatePollInterval()
To run the proof.
-------------
* Add `cbmc`, `goto-cc`, `goto-instrument`, `goto-analyzer`, and `cbmc-viewer`
to your path.
* Run `make`.
* Open html/index.html in a web browser.
To use [`arpa`](https://github.com/awslabs/aws-proof-build-assistant) to simplify writing Makefiles.
-------------
* Run `make arpa` to generate a Makefile.arpa that contains relevant build information for the proof.
* Use Makefile.arpa as the starting point for your proof Makefile by:
1. Modifying Makefile.arpa (if required).
2. Including Makefile.arpa into the existing proof Makefile (add `sinclude Makefile.arpa` at the bottom of the Makefile, right before `include ../Makefile.common`).

View File

@ -0,0 +1,43 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file Sntp_CalculatePollInterval_harness.c
* @brief Implements the proof harness for Sntp_CalculatePollInterval function.
*/
#include "core_sntp_serializer.h"
void harness()
{
uint16_t clockFreqTolerance;
uint16_t desiredAccuracy;
uint32_t * pPollInterval;
SntpStatus_t sntpStatus;
pPollInterval = malloc( sizeof( uint32_t ) );
sntpStatus = Sntp_CalculatePollInterval( clockFreqTolerance, desiredAccuracy, pPollInterval );
__CPROVER_assert( ( sntpStatus == SntpErrorBadParameter || sntpStatus == SntpSuccess || sntpStatus == SntpZeroPollInterval ), "The return value is not a valid SNTP status." );
}

View File

@ -0,0 +1 @@
# This file marks this directory as containing a CBMC proof.

View File

@ -0,0 +1,7 @@
{ "expected-missing-functions":
[
],
"proof-name": "Sntp_CalculatePollInterval",
"proof-root": "test/cbmc/proofs"
}

View File

@ -0,0 +1,20 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
HARNESS_ENTRY = harness
HARNESS_FILE = Sntp_ConvertToUnixTime_harness
# This should be a unique identifier for this proof, and will appear on the
# Litani dashboard. It can be human-readable and contain spaces if you wish.
PROOF_UID = Sntp_ConvertToUnixTime
DEFINES +=
INCLUDES +=
REMOVE_FUNCTION_BODY +=
UNWINDSET +=
PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
PROJECT_SOURCES += $(SRCDIR)/source/core_sntp_serializer.c
include ../Makefile.common

View File

@ -0,0 +1,23 @@
Sntp_ConvertToUnixTime proof
==============
This directory contains a memory safety proof for Sntp_ConvertToUnixTime.
The proof runs within 3 minutes on a t2.2xlarge. It provides complete coverage of:
* Sntp_ConvertToUnixTime()
To run the proof.
-------------
* Add `cbmc`, `goto-cc`, `goto-instrument`, `goto-analyzer`, and `cbmc-viewer`
to your path.
* Run `make`.
* Open html/index.html in a web browser.
To use [`arpa`](https://github.com/awslabs/aws-proof-build-assistant) to simplify writing Makefiles.
-------------
* Run `make arpa` to generate a Makefile.arpa that contains relevant build information for the proof.
* Use Makefile.arpa as the starting point for your proof Makefile by:
1. Modifying Makefile.arpa (if required).
2. Including Makefile.arpa into the existing proof Makefile (add `sinclude Makefile.arpa` at the bottom of the Makefile, right before `include ../Makefile.common`).

View File

@ -0,0 +1,46 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file Sntp_ConvertToUnixTime_harness.c
* @brief Implements the proof harness for Sntp_ConvertToUnixTime function.
*/
#include "core_sntp_serializer.h"
void harness()
{
SntpTimestamp_t * pSntpTime;
uint32_t * pUnixTimeSecs;
uint32_t * pUnixTimeMicrosecs;
SntpStatus_t sntpStatus;
pSntpTime = malloc( sizeof( SntpTimestamp_t ) );
pUnixTimeSecs = malloc( sizeof( uint32_t ) );
pUnixTimeMicrosecs = malloc( sizeof( uint32_t ) );
sntpStatus = Sntp_ConvertToUnixTime( pSntpTime, pUnixTimeSecs, pUnixTimeMicrosecs );
__CPROVER_assert( ( sntpStatus == SntpErrorBadParameter || sntpStatus == SntpErrorTimeNotSupported || sntpStatus == SntpSuccess ), "The return value is not a valid SNTP Status" );
}

View File

@ -0,0 +1 @@
# This file marks this directory as containing a CBMC proof.

View File

@ -0,0 +1,7 @@
{ "expected-missing-functions":
[
],
"proof-name": "Sntp_ConvertToUnixTime",
"proof-root": "test/cbmc/proofs"
}

View File

@ -0,0 +1,20 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
HARNESS_ENTRY = harness
HARNESS_FILE = Sntp_DeserializeResponse_harness
# This should be a unique identifier for this proof, and will appear on the
# Litani dashboard. It can be human-readable and contain spaces if you wish.
PROOF_UID = Sntp_DeserializeResponse
DEFINES +=
INCLUDES +=
REMOVE_FUNCTION_BODY +=
UNWINDSET +=
PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
PROJECT_SOURCES += $(SRCDIR)/source/core_sntp_serializer.c
include ../Makefile.common

View File

@ -0,0 +1,20 @@
Sntp_DeserializeResponse proof
==============
This directory contains a memory safety proof for Sntp_DeserializeResponse.
To run the proof.
-------------
* Add `cbmc`, `goto-cc`, `goto-instrument`, `goto-analyzer`, and `cbmc-viewer`
to your path.
* Run `make`.
* Open html/index.html in a web browser.
To use [`arpa`](https://github.com/awslabs/aws-proof-build-assistant) to simplify writing Makefiles.
-------------
* Run `make arpa` to generate a Makefile.arpa that contains relevant build information for the proof.
* Use Makefile.arpa as the starting point for your proof Makefile by:
1. Modifying Makefile.arpa (if required).
2. Including Makefile.arpa into the existing proof Makefile (add `sinclude Makefile.arpa` at the bottom of the Makefile, right before `include ../Makefile.common`).

View File

@ -0,0 +1,54 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file Sntp_DeserializeResponse_harness.c
* @brief Implements the proof harness for Sntp_DeserializeResponse function.
*/
#include <stdint.h>
#include "core_sntp_serializer.h"
void harness()
{
SntpTimestamp_t * pRequestTime;
SntpTimestamp_t * pResponseRxTime;
void * pResponseBuffer;
size_t bufferSize;
SntpResponseData_t * pParsedResponse;
SntpStatus_t sntpStatus;
__CPROVER_assume( bufferSize < CBMC_MAX_OBJECT_SIZE );
pRequestTime = malloc( sizeof( SntpTimestamp_t ) );
pResponseRxTime = malloc( sizeof( SntpTimestamp_t ) );
pResponseBuffer = malloc( bufferSize );
pParsedResponse = malloc( sizeof( SntpResponseData_t ) );
sntpStatus = Sntp_DeserializeResponse( pRequestTime, pResponseRxTime, pResponseBuffer, bufferSize, pParsedResponse );
__CPROVER_assert( ( sntpStatus == SntpErrorBadParameter ) || ( sntpStatus == SntpErrorBufferTooSmall ) ||
( sntpStatus == SntpInvalidResponse ) || ( sntpStatus == SntpSuccess ) || ( sntpStatus == SntpRejectedResponseChangeServer ) ||
( sntpStatus == SntpRejectedResponseRetryWithBackoff ) || ( sntpStatus == SntpRejectedResponseOtherCode ), "This is a valid sntp return status" );
}

View File

@ -0,0 +1 @@
# This file marks this directory as containing a CBMC proof.

View File

@ -0,0 +1,7 @@
{ "expected-missing-functions":
[
],
"proof-name": "Sntp_DeserializeResponse",
"proof-root": "test/cbmc/proofs"
}

View File

@ -0,0 +1,20 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
HARNESS_ENTRY = harness
HARNESS_FILE = Sntp_Init_harness
# This should be a unique identifier for this proof, and will appear on the
# Litani dashboard. It can be human-readable and contain spaces if you wish.
PROOF_UID = Sntp_Init
DEFINES +=
INCLUDES +=
REMOVE_FUNCTION_BODY +=
UNWINDSET +=
PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
PROJECT_SOURCES += $(SRCDIR)/source/core_sntp_client.c
include ../Makefile.common

View File

@ -0,0 +1,15 @@
Sntp_Init proof
==============
This directory contains a memory safety proof for Sntp_Init.
The proof runs within 3 minutes on a t2.2xlarge. It provides complete coverage of:
* Sntp_Init()
To run the proof.
-------------
* Add `cbmc`, `goto-cc`, `goto-instrument`, `goto-analyzer`, and `cbmc-viewer`
to your path.
* Run `make`.
* Open html/index.html in a web browser.

View File

@ -0,0 +1,62 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file Sntp_Init_harness.c
* @brief Implements the proof harness for Sntp_Init function.
*/
#include <stddef.h>
#include "core_sntp_client.h"
void harness()
{
SntpContext_t * pContext;
SntpServerInfo_t * pTimeServers;
size_t numOfServers;
uint32_t serverResponseTimeoutMs;
uint8_t * pNetworkBuffer;
size_t bufferSize;
SntpResolveDns_t resolveDnsFunc;
SntpGetTime_t getSystemTimeFunc;
SntpSetTime_t setSystemTimeFunc;
UdpTransportInterface_t * pTransportIntf;
SntpAuthenticationInterface_t * pAuthIntf;
SntpStatus_t sntpStatus;
pContext = malloc( sizeof( SntpContext_t ) );
pTimeServers = malloc( sizeof( SntpServerInfo_t ) );
__CPROVER_assume( bufferSize < CBMC_MAX_OBJECT_SIZE );
pNetworkBuffer = malloc( bufferSize );
pTransportIntf = malloc( sizeof( UdpTransportInterface_t ) );
pAuthIntf = malloc( sizeof( SntpAuthenticationInterface_t ) );
sntpStatus = Sntp_Init( pContext, pTimeServers, numOfServers, serverResponseTimeoutMs, pNetworkBuffer,
bufferSize, resolveDnsFunc, getSystemTimeFunc, setSystemTimeFunc,
pTransportIntf, pAuthIntf );
__CPROVER_assert( ( sntpStatus == SntpErrorBadParameter || sntpStatus == SntpSuccess || sntpStatus == SntpErrorBufferTooSmall ), "The return value is not a valid SNTP Status" );
}

View File

@ -0,0 +1 @@
# This file marks this directory as containing a CBMC proof.

View File

@ -0,0 +1,7 @@
{ "expected-missing-functions":
[
],
"proof-name": "Sntp_Init",
"proof-root": "test/cbmc/proofs"
}

View File

@ -0,0 +1,43 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
HARNESS_ENTRY = harness
HARNESS_FILE = Sntp_ReceiveTimeResponse_harness
# Please see test/cbmc/stubs/core_sntp_stubs.c for
# more information on MAX_NETWORK_RECV_TRIES.
MAX_NETWORK_RECV_TRIES=5
# Bound on the timeout in Sntp_ReceiveTimeResponse. This timeout is bounded because
# memory saftey can be proven in a only a single iteration.
# Each iteration will try to receive a single packet in its entirey. With a time
# out of 1 we can get coverage of the entire function. Another iteration will
# performed unnecessarily duplicating of the proof.
SNTP_RECEIVE_TIMEOUT=1
# Maximum number of sntp time servers
MAX_NO_OF_SERVERS=5
# Maximum number of attempts in outer loop of Sntp_ReceiveTimeResponse needed to receive a response from server.
MAX_ITERATIONS_RECEIVE_RESPONSE=1
# This should be a unique identifier for this proof, and will appear on the
# Litani dashboard. It can be human-readable and contain spaces if you wish.
PROOF_UID = Sntp_ReceiveTimeResponse
DEFINES +=-DMAX_NO_OF_SERVERS=$(MAX_NO_OF_SERVERS)
DEFINES +=-DSNTP_RECEIVE_TIMEOUT=$(SNTP_RECEIVE_TIMEOUT)
INCLUDES +=
REMOVE_FUNCTION_BODY +=Sntp_DeserializeResponse
UNWINDSET +=__CPROVER_file_local_core_sntp_client_c_receiveSntpResponse.0:$(shell expr $(MAX_NETWORK_RECV_TRIES) + 1 )
UNWINDSET +=__CPROVER_file_local_core_sntp_client_c_Sntp_ReceiveTimeResponse.0:$(shell expr $(MAX_ITERATIONS_RECEIVE_RESPONSE) + 1 )
UNWINDSET +=unconstrainedCoreSntpContext.0:$(shell expr $(MAX_NO_OF_SERVERS) + 1 )
PROOF_SOURCES += $(SRCDIR)/test/cbmc/sources/core_sntp_cbmc_state.c
PROOF_SOURCES += $(SRCDIR)/test/cbmc/stubs/core_sntp_stubs.c
PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
PROJECT_SOURCES += $(SRCDIR)/source/core_sntp_client.c
include ../Makefile.common

View File

@ -0,0 +1,20 @@
Sntp_ReceiveTimeResponse proof
==============
This directory contains a memory safety proof for Sntp_ReceiveTimeResponse.
To run the proof.
-------------
* Add `cbmc`, `goto-cc`, `goto-instrument`, `goto-analyzer`, and `cbmc-viewer`
to your path.
* Run `make`.
* Open html/index.html in a web browser.
To use [`arpa`](https://github.com/awslabs/aws-proof-build-assistant) to simplify writing Makefiles.
-------------
* Run `make arpa` to generate a Makefile.arpa that contains relevant build information for the proof.
* Use Makefile.arpa as the starting point for your proof Makefile by:
1. Modifying Makefile.arpa (if required).
2. Including Makefile.arpa into the existing proof Makefile (add `sinclude Makefile.arpa` at the bottom of the Makefile, right before `include ../Makefile.common`).

View File

@ -0,0 +1,62 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file Sntp_ReceiveTimeResponse_harness.c
* @brief Implements the proof harness for Sntp_ReceiveTimeResponse function.
*/
#include <stddef.h>
#include "core_sntp_cbmc_state.h"
#include "core_sntp_stubs.h"
#include "core_sntp_client.h"
void harness()
{
SntpContext_t * pContext;
uint32_t blockTimeMs;
SntpStatus_t sntpStatus;
pContext = unconstrainedCoreSntpContext();
if( pContext != NULL )
{
/* Setting the initial value of request time to check for response timeout
* while reading data from network. */
GetTimeFuncStub( &pContext->lastRequestTime );
}
/* The SNTP_RECEIVE_TIMEOUT is used here to control the number of loops
* when receiving on the network. The default is used here because memory
* safety can be proven in only a few iterations. Please see this proof's
* Makefile for more information. */
__CPROVER_assume( blockTimeMs < SNTP_RECEIVE_TIMEOUT );
sntpStatus = Sntp_ReceiveTimeResponse( pContext, blockTimeMs );
__CPROVER_assert( ( sntpStatus == SntpErrorBadParameter || sntpStatus == SntpSuccess ||
sntpStatus == SntpNoResponseReceived || sntpStatus == SntpRejectedResponse ||
sntpStatus == SntpErrorResponseTimeout || sntpStatus == SntpErrorNetworkFailure ),
"The return value is not a valid coreSNTP Status" );
}

View File

@ -0,0 +1 @@
# This file marks this directory as containing a CBMC proof.

View File

@ -0,0 +1,7 @@
{ "expected-missing-functions":
[
],
"proof-name": "Sntp_ReceiveTimeResponse",
"proof-root": "test/cbmc/proofs"
}

View File

@ -0,0 +1,40 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
HARNESS_ENTRY = harness
HARNESS_FILE = Sntp_SendTimeRequest_harness
# Please see test/cbmc/stubs/core_sntp_stubs.c for
# more information on MAX_NETWORK_SEND_TRIES.
MAX_NETWORK_SEND_TRIES=3
# Bound on the timeout in Sntp_ReceiveTimeResponse. This timeout is bounded because
# memory saftey can be proven in a only a single iteration.
# Each iteration will try to receive a single packet in its entirey. With a time
# out of 1 we can get coverage of the entire function. Another iteration will
# performed unnecessarily duplicating of the proof.
SNTP_SEND_TIMEOUT=1
# Maximum number of sntp time servers
MAX_NO_OF_SERVERS=5
# This should be a unique identifier for this proof, and will appear on the
# Litani dashboard. It can be human-readable and contain spaces if you wish.
PROOF_UID = Sntp_SendTimeRequest
DEFINES +=-DMAX_NO_OF_SERVERS=$(MAX_NO_OF_SERVERS)
DEFINES +=-DSNTP_SEND_TIMEOUT=$(SNTP_SEND_TIMEOUT)
INCLUDES +=
# Providing a stub for this function as we have already have a separate proof
# for this function
REMOVE_FUNCTION_BODY +=Sntp_SerializeRequest
UNWINDSET +=__CPROVER_file_local_core_sntp_client_c_sendSntpPacket.0:$(MAX_NETWORK_SEND_TRIES)
UNWINDSET +=unconstrainedCoreSntpContext.0:$(shell expr $(MAX_NO_OF_SERVERS) + 1 )
PROOF_SOURCES += $(SRCDIR)/test/cbmc/sources/core_sntp_cbmc_state.c
PROOF_SOURCES += $(SRCDIR)/test/cbmc/stubs/core_sntp_stubs.c
PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
PROJECT_SOURCES += $(SRCDIR)/source/core_sntp_client.c
include ../Makefile.common

View File

@ -0,0 +1,19 @@
Sntp_SendTimeRequest proof
==============
This directory contains a memory safety proof for Sntp_SendTimeRequest.
To run the proof.
-------------
* Add `cbmc`, `goto-cc`, `goto-instrument`, `goto-analyzer`, and `cbmc-viewer`
to your path.
* Run `make`.
* Open html/index.html in a web browser.
-------------
* Run `make arpa` to generate a Makefile.arpa that contains relevant build information for the proof.
* Use Makefile.arpa as the starting point for your proof Makefile by:
1. Modifying Makefile.arpa (if required).
2. Including Makefile.arpa into the existing proof Makefile (add `sinclude Makefile.arpa` at the bottom of the Makefile, right before `include ../Makefile.common`).

View File

@ -0,0 +1,56 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file Sntp_SendTimeRequest_harness.c
* @brief Implements the proof harness for Sntp_SendTimeRequest function.
*/
#include <stddef.h>
#include "core_sntp_client.h"
#include "core_sntp_cbmc_state.h"
void harness()
{
SntpContext_t * pContext;
uint32_t randomNumber;
SntpStatus_t sntpStatus;
uint32_t blockTimeMs;
pContext = unconstrainedCoreSntpContext();
/* The SNTP_SEND_TIMEOUT is used here to control the number of loops
* when sending data on the network. The default is used here because memory
* safety can be proven in only a few iterations. Please see this proof's
* Makefile for more information. */
__CPROVER_assume( blockTimeMs < SNTP_SEND_TIMEOUT );
sntpStatus = Sntp_SendTimeRequest( pContext, randomNumber, blockTimeMs );
__CPROVER_assert( ( sntpStatus == SntpErrorBadParameter || sntpStatus == SntpSuccess ||
sntpStatus == SntpErrorContextNotInitialized || sntpStatus == SntpErrorSendTimeout ||
sntpStatus == SntpErrorBufferTooSmall || sntpStatus == SntpErrorDnsFailure ||
sntpStatus == SntpErrorAuthFailure || sntpStatus == SntpErrorNetworkFailure ),
"The return value is not a valid coreSNTP Status" );
}

View File

@ -0,0 +1 @@
# This file marks this directory as containing a CBMC proof.

View File

@ -0,0 +1,7 @@
{ "expected-missing-functions":
[
],
"proof-name": "Sntp_SendTimeRequest",
"proof-root": "test/cbmc/proofs"
}

View File

@ -0,0 +1,20 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
HARNESS_ENTRY = harness
HARNESS_FILE = Sntp_SerializeRequest_harness
# This should be a unique identifier for this proof, and will appear on the
# Litani dashboard. It can be human-readable and contain spaces if you wish.
PROOF_UID = Sntp_SerializeRequest
DEFINES +=
INCLUDES +=
REMOVE_FUNCTION_BODY +=
UNWINDSET +=
PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
PROJECT_SOURCES += $(SRCDIR)/source/core_sntp_serializer.c
include ../Makefile.common

View File

@ -0,0 +1,23 @@
Sntp_SerializeRequest proof
==============
This directory contains a memory safety proof for Sntp_SerializeRequest.
The proof runs within 3 minutes on a t2.2xlarge. It provides complete coverage of:
* Sntp_SerializeRequest()
To run the proof.
-------------
* Add `cbmc`, `goto-cc`, `goto-instrument`, `goto-analyzer`, and `cbmc-viewer`
to your path.
* Run `make`.
* Open html/index.html in a web browser.
To use [`arpa`](https://github.com/awslabs/aws-proof-build-assistant) to simplify writing Makefiles.
-------------
* Run `make arpa` to generate a Makefile.arpa that contains relevant build information for the proof.
* Use Makefile.arpa as the starting point for your proof Makefile by:
1. Modifying Makefile.arpa (if required).
2. Including Makefile.arpa into the existing proof Makefile (add `sinclude Makefile.arpa` at the bottom of the Makefile, right before `include ../Makefile.common`).

View File

@ -0,0 +1,50 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file Sntp_SerializeRequest_harness.c
* @brief Implements the proof harness for Sntp_SerializeRequest function.
*/
#include <stdint.h>
#include "core_sntp_serializer.h"
void harness()
{
SntpTimestamp_t * pRequestTime;
uint32_t randomNumber;
void * pBuffer;
size_t bufferSize;
SntpStatus_t sntpStatus;
pRequestTime = malloc( sizeof( SntpTimestamp_t ) );
__CPROVER_assume( bufferSize < CBMC_MAX_OBJECT_SIZE );
pBuffer = malloc( bufferSize );
sntpStatus = Sntp_SerializeRequest( pRequestTime, randomNumber, pBuffer, bufferSize );
__CPROVER_assert( ( sntpStatus == SntpErrorBadParameter || sntpStatus == SntpErrorBufferTooSmall || sntpStatus == SntpSuccess ), "The return value is not a valid SNTP Status" );
}

View File

@ -0,0 +1 @@
# This file marks this directory as containing a CBMC proof.

View File

@ -0,0 +1,7 @@
{ "expected-missing-functions":
[
],
"proof-name": "Sntp_SerializeRequest",
"proof-root": "test/cbmc/proofs"
}

View File

@ -0,0 +1,92 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
import json
import logging
def _get_max_length_per_column_list(data):
ret = [len(item) + 1 for item in data[0]]
for row in data[1:]:
for idx, item in enumerate(row):
ret[idx] = max(ret[idx], len(item) + 1)
return ret
def _get_table_header_separator(max_length_per_column_list):
line_sep = ""
for max_length_of_word_in_col in max_length_per_column_list:
line_sep += "|" + "-" * (max_length_of_word_in_col + 1)
line_sep += "|\n"
return line_sep
def _get_entries(max_length_per_column_list, row_data):
entries = []
for row in row_data:
entry = ""
for idx, word in enumerate(row):
max_length_of_word_in_col = max_length_per_column_list[idx]
space_formatted_word = (max_length_of_word_in_col - len(word)) * " "
entry += "| " + word + space_formatted_word
entry += "|\n"
entries.append(entry)
return entries
def _get_rendered_table(data):
table = []
max_length_per_column_list = _get_max_length_per_column_list(data)
entries = _get_entries(max_length_per_column_list, data)
for idx, entry in enumerate(entries):
if idx == 1:
line_sep = _get_table_header_separator(max_length_per_column_list)
table.append(line_sep)
table.append(entry)
table.append("\n")
return "".join(table)
def _get_status_and_proof_summaries(run_dict):
"""Parse a dict representing a Litani run and create lists summarizing the
proof results.
Parameters
----------
run_dict
A dictionary representing a Litani run.
Returns
-------
A list of 2 lists.
The first sub-list maps a status to the number of proofs with that status.
The second sub-list maps each proof to its status.
"""
count_statuses = {}
proofs = [["Proof", "Status"]]
for proof_pipeline in run_dict["pipelines"]:
status_pretty_name = proof_pipeline["status"].title().replace("_", " ")
try:
count_statuses[status_pretty_name] += 1
except KeyError:
count_statuses[status_pretty_name] = 1
proof = proof_pipeline["name"]
proofs.append([proof, status_pretty_name])
statuses = [["Status", "Count"]]
for status, count in count_statuses.items():
statuses.append([status, str(count)])
return [statuses, proofs]
def print_proof_results(out_file):
"""
Print 2 strings that summarize the proof results.
When printing, each string will render as a GitHub flavored Markdown table.
"""
try:
with open(out_file, encoding='utf-8') as run_json:
run_dict = json.load(run_json)
for summary in _get_status_and_proof_summaries(run_dict):
print(_get_rendered_table(summary))
except Exception as ex: # pylint: disable=broad-except
logging.critical("Could not print results. Exception: %s", str(ex))

View File

@ -0,0 +1,414 @@
#!/usr/bin/env python3
#
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
import argparse
import asyncio
import json
import logging
import math
import os
import pathlib
import re
import subprocess
import sys
import tempfile
from lib.summarize import print_proof_results
DESCRIPTION = "Configure and run all CBMC proofs in parallel"
# Keep the epilog hard-wrapped at 70 characters, as it gets printed
# verbatim in the terminal. 70 characters stops here --------------> |
EPILOG = """
This tool automates the process of running `make report` in each of
the CBMC proof directories. The tool calculates the dependency graph
of all tasks needed to build, run, and report on all the proofs, and
executes these tasks in parallel.
The tool is roughly equivalent to doing this:
litani init --project "my-cool-project";
find . -name cbmc-proof.txt | while read -r proof; do
pushd $(dirname ${proof});
# The `make _report` rule adds a single proof to litani
# without running it
make _report;
popd;
done
litani run-build;
except that it is much faster and provides some convenience options.
The CBMC CI runs this script with no arguments to build and run all
proofs in parallel. The value of "my-cool-project" is taken from the
PROJECT_NAME variable in Makefile-project-defines.
The --no-standalone argument omits the `litani init` and `litani
run-build`; use it when you want to add additional proof jobs, not
just the CBMC ones. In that case, you would run `litani init`
yourself; then run `run-cbmc-proofs --no-standalone`; add any
additional jobs that you want to execute with `litani add-job`; and
finally run `litani run-build`.
The litani dashboard will be written under the `output` directory; the
cbmc-viewer reports remain in the `$PROOF_DIR/report` directory. The
HTML dashboard from the latest Litani run will always be symlinked to
`output/latest/html/index.html`, so you can keep that page open in
your browser and reload the page whenever you re-run this script.
"""
# 70 characters stops here ----------------------------------------> |
def get_project_name():
cmd = [
"make",
"--no-print-directory",
"-f", "Makefile.common",
"echo-project-name",
]
logging.debug(" ".join(cmd))
proc = subprocess.run(cmd, universal_newlines=True, stdout=subprocess.PIPE, check=False)
if proc.returncode:
logging.critical("could not run make to determine project name")
sys.exit(1)
if not proc.stdout.strip():
logging.warning(
"project name has not been set; using generic name instead. "
"Set the PROJECT_NAME value in Makefile-project-defines to "
"remove this warning")
return "<PROJECT NAME HERE>"
return proc.stdout.strip()
def get_args():
pars = argparse.ArgumentParser(
description=DESCRIPTION, epilog=EPILOG,
formatter_class=argparse.RawDescriptionHelpFormatter)
for arg in [{
"flags": ["-j", "--parallel-jobs"],
"type": int,
"metavar": "N",
"help": "run at most N proof jobs in parallel",
}, {
"flags": ["--fail-on-proof-failure"],
"action": "store_true",
"help": "exit with return code `10' if any proof failed"
" (default: exit 0)",
}, {
"flags": ["--no-standalone"],
"action": "store_true",
"help": "only configure proofs: do not initialize nor run",
}, {
"flags": ["-p", "--proofs"],
"nargs": "+",
"metavar": "DIR",
"help": "only run proof in directory DIR (can pass more than one)",
}, {
"flags": ["--project-name"],
"metavar": "NAME",
"default": get_project_name(),
"help": "project name for report. Default: %(default)s",
}, {
"flags": ["--marker-file"],
"metavar": "FILE",
"default": "cbmc-proof.txt",
"help": (
"name of file that marks proof directories. Default: "
"%(default)s"),
}, {
"flags": ["--no-memory-profile"],
"action": "store_true",
"help": "disable memory profiling, even if Litani supports it"
}, {
"flags": ["--no-expensive-limit"],
"action": "store_true",
"help": "do not limit parallelism of 'EXPENSIVE' jobs",
}, {
"flags": ["--expensive-jobs-parallelism"],
"metavar": "N",
"default": 1,
"type": int,
"help": (
"how many proof jobs marked 'EXPENSIVE' to run in parallel. "
"Default: %(default)s"),
}, {
"flags": ["--verbose"],
"action": "store_true",
"help": "verbose output",
}, {
"flags": ["--debug"],
"action": "store_true",
"help": "debug output",
}, {
"flags": ["--summarize"],
"action": "store_true",
"help": "summarize proof results with two tables on stdout",
}, {
"flags": ["--version"],
"action": "version",
"version": "CBMC starter kit 2.5",
"help": "display version and exit"
}]:
flags = arg.pop("flags")
pars.add_argument(*flags, **arg)
return pars.parse_args()
def set_up_logging(verbose):
if verbose:
level = logging.DEBUG
else:
level = logging.WARNING
logging.basicConfig(
format="run-cbmc-proofs: %(message)s", level=level)
def task_pool_size():
ret = os.cpu_count()
if ret is None or ret < 3:
return 1
return ret - 2
def print_counter(counter):
# pylint: disable=consider-using-f-string
print("\rConfiguring CBMC proofs: "
"{complete:{width}} / {total:{width}}".format(**counter), end="", file=sys.stderr)
def get_proof_dirs(proof_root, proof_list, marker_file):
if proof_list is not None:
proofs_remaining = list(proof_list)
else:
proofs_remaining = []
for root, _, fyles in os.walk(proof_root):
proof_name = str(pathlib.Path(root).name)
if root != str(proof_root) and ".litani_cache_dir" in fyles:
pathlib.Path(f"{root}/.litani_cache_dir").unlink()
if proof_list and proof_name not in proof_list:
continue
if proof_list and proof_name in proofs_remaining:
proofs_remaining.remove(proof_name)
if marker_file in fyles:
yield root
if proofs_remaining:
logging.critical(
"The following proofs were not found: %s",
", ".join(proofs_remaining))
sys.exit(1)
def run_build(litani, jobs, fail_on_proof_failure, summarize):
cmd = [str(litani), "run-build"]
if jobs:
cmd.extend(["-j", str(jobs)])
if fail_on_proof_failure:
cmd.append("--fail-on-pipeline-failure")
if summarize:
out_file = pathlib.Path(tempfile.gettempdir(), "run.json").resolve()
cmd.extend(["--out-file", str(out_file)])
logging.debug(" ".join(cmd))
proc = subprocess.run(cmd, check=False)
if proc.returncode and not fail_on_proof_failure:
logging.critical("Failed to run litani run-build")
sys.exit(1)
if summarize:
print_proof_results(out_file)
out_file.unlink()
if proc.returncode:
logging.error("One or more proofs failed")
sys.exit(10)
def get_litani_path(proof_root):
cmd = [
"make",
"--no-print-directory",
f"PROOF_ROOT={proof_root}",
"-f", "Makefile.common",
"litani-path",
]
logging.debug(" ".join(cmd))
proc = subprocess.run(cmd, universal_newlines=True, stdout=subprocess.PIPE, check=False)
if proc.returncode:
logging.critical("Could not determine path to litani")
sys.exit(1)
return proc.stdout.strip()
def get_litani_capabilities(litani_path):
cmd = [litani_path, "print-capabilities"]
proc = subprocess.run(
cmd, text=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=False)
if proc.returncode:
return []
try:
return json.loads(proc.stdout)
except RuntimeError:
logging.warning("Could not load litani capabilities: '%s'", proc.stdout)
return []
def check_uid_uniqueness(proof_dir, proof_uids):
with (pathlib.Path(proof_dir) / "Makefile").open() as handle:
for line in handle:
match = re.match(r"^PROOF_UID\s*=\s*(?P<uid>\w+)", line)
if not match:
continue
if match["uid"] not in proof_uids:
proof_uids[match["uid"]] = proof_dir
return
logging.critical(
"The Makefile in directory '%s' should have a different "
"PROOF_UID than the Makefile in directory '%s'",
proof_dir, proof_uids[match["uid"]])
sys.exit(1)
logging.critical(
"The Makefile in directory '%s' should contain a line like", proof_dir)
logging.critical("PROOF_UID = ...")
logging.critical("with a unique identifier for the proof.")
sys.exit(1)
def should_enable_memory_profiling(litani_caps, args):
if args.no_memory_profile:
return False
return "memory_profile" in litani_caps
def should_enable_pools(litani_caps, args):
if args.no_expensive_limit:
return False
return "pools" in litani_caps
async def configure_proof_dirs( # pylint: disable=too-many-arguments
queue, counter, proof_uids, enable_pools, enable_memory_profiling, debug):
while True:
print_counter(counter)
path = str(await queue.get())
check_uid_uniqueness(path, proof_uids)
pools = ["ENABLE_POOLS=true"] if enable_pools else []
profiling = [
"ENABLE_MEMORY_PROFILING=true"] if enable_memory_profiling else []
# Allow interactive tasks to preempt proof configuration
proc = await asyncio.create_subprocess_exec(
"nice", "-n", "15", "make", *pools,
*profiling, "-B", "_report", "" if debug else "--quiet", cwd=path,
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
stdout, stderr = await proc.communicate()
logging.debug("returncode: %s", str(proc.returncode))
logging.debug("stdout:")
for line in stdout.decode().splitlines():
logging.debug(line)
logging.debug("stderr:")
for line in stderr.decode().splitlines():
logging.debug(line)
counter["fail" if proc.returncode else "pass"].append(path)
counter["complete"] += 1
print_counter(counter)
queue.task_done()
async def main(): # pylint: disable=too-many-locals
args = get_args()
set_up_logging(args.verbose)
proof_root = pathlib.Path(os.getcwd())
litani = get_litani_path(proof_root)
litani_caps = get_litani_capabilities(litani)
enable_pools = should_enable_pools(litani_caps, args)
init_pools = [
"--pools", f"expensive:{args.expensive_jobs_parallelism}"
] if enable_pools else []
if not args.no_standalone:
cmd = [
str(litani), "init", *init_pools, "--project", args.project_name,
"--no-print-out-dir",
]
if "output_directory_flags" in litani_caps:
out_prefix = proof_root / "output"
out_symlink = out_prefix / "latest"
out_index = out_symlink / "html" / "index.html"
cmd.extend([
"--output-prefix", str(out_prefix),
"--output-symlink", str(out_symlink),
])
print(
"\nFor your convenience, the output of this run will be symbolically linked to ",
out_index, "\n")
logging.debug(" ".join(cmd))
proc = subprocess.run(cmd, check=False)
if proc.returncode:
logging.critical("Failed to run litani init")
sys.exit(1)
proof_dirs = list(get_proof_dirs(
proof_root, args.proofs, args.marker_file))
if not proof_dirs:
logging.critical("No proof directories found")
sys.exit(1)
proof_queue = asyncio.Queue()
for proof_dir in proof_dirs:
proof_queue.put_nowait(proof_dir)
counter = {
"pass": [],
"fail": [],
"complete": 0,
"total": len(proof_dirs),
"width": int(math.log10(len(proof_dirs))) + 1
}
proof_uids = {}
tasks = []
enable_memory_profiling = should_enable_memory_profiling(litani_caps, args)
for _ in range(task_pool_size()):
task = asyncio.create_task(configure_proof_dirs(
proof_queue, counter, proof_uids, enable_pools,
enable_memory_profiling, args.debug))
tasks.append(task)
await proof_queue.join()
print_counter(counter)
print("", file=sys.stderr)
if counter["fail"]:
logging.critical(
"Failed to configure the following proofs:\n%s", "\n".join(
[str(f) for f in counter["fail"]]))
sys.exit(1)
if not args.no_standalone:
run_build(litani, args.parallel_jobs, args.fail_on_proof_failure, args.summarize)
if __name__ == "__main__":
asyncio.run(main())

View File

@ -0,0 +1,6 @@
CBMC proof source code
======================
This directory contains source code written for CBMC proofs. It is
common to write some code to model aspects of the system under test,
and this code goes here.

View File

@ -0,0 +1,113 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_cbmc_state.c
* @brief Implements the functions defined in core_sntp_cbmc_state.h.
*/
#include <stdint.h>
#include <stdlib.h>
#include "core_sntp_client.h"
#include "core_sntp_cbmc_state.h"
#include "core_sntp_stubs.h"
SntpContext_t * unconstrainedCoreSntpContext()
{
SntpServerInfo_t * pTimeServers;
SntpContext_t * pContext;
size_t currentServerIndex;
size_t numOfServers;
uint32_t serverResponseTimeoutMs;
uint8_t * pNetworkBuffer;
size_t bufferSize;
UdpTransportInterface_t * pNetworkIntf;
SntpAuthenticationInterface_t * pAuthIntf;
SntpStatus_t sntpStatus = SntpSuccess;
pContext = malloc( sizeof( SntpContext_t ) );
__CPROVER_assume( numOfServers < MAX_NO_OF_SERVERS );
__CPROVER_assume( serverResponseTimeoutMs < CBMC_MAX_OBJECT_SIZE );
__CPROVER_assume( currentServerIndex < CBMC_MAX_OBJECT_SIZE );
if( numOfServers == 0 )
{
pTimeServers = NULL;
}
else
{
pTimeServers = malloc( numOfServers * sizeof( SntpServerInfo_t ) );
}
if( pTimeServers != NULL )
{
for( size_t i = 0; i < numOfServers; i++ )
{
__CPROVER_assume( pTimeServers[ i ].serverNameLen < CBMC_MAX_OBJECT_SIZE );
__CPROVER_assume( pTimeServers[ i ].port < CBMC_MAX_OBJECT_SIZE );
pTimeServers[ i ].pServerName = malloc( pTimeServers[ i ].serverNameLen );
}
}
__CPROVER_assume( bufferSize < CBMC_MAX_OBJECT_SIZE );
pNetworkBuffer = malloc( bufferSize );
pNetworkIntf = malloc( sizeof( UdpTransportInterface_t ) );
if( pNetworkIntf != NULL )
{
pNetworkIntf->pUserContext = malloc( sizeof( NetworkContext_t ) );
pNetworkIntf->sendTo = NetworkInterfaceSendStub;
pNetworkIntf->recvFrom = NetworkInterfaceReceiveStub;
}
pAuthIntf = malloc( sizeof( SntpAuthenticationInterface_t ) );
if( pAuthIntf != NULL )
{
pAuthIntf->pAuthContext = malloc( sizeof( SntpAuthContext_t ) );
pAuthIntf->generateClientAuth = GenerateClientAuthStub;
pAuthIntf->validateServerAuth = ValidateServerAuthStub;
}
/* It is part of the API contract to call Sntp_Init() with the SntpContext_t
* before any other function in core_sntp_client.h. */
if( pContext != NULL )
{
pContext->currentServerIndex = currentServerIndex;
sntpStatus = Sntp_Init( pContext, pTimeServers, numOfServers, serverResponseTimeoutMs, pNetworkBuffer,
bufferSize, ResolveDnsFuncStub, GetTimeFuncStub, SetTimeFuncStub,
pNetworkIntf, pAuthIntf );
}
/* If the SntpContext_t initialization failed, then set the context to NULL
* so that function under harness will return immediately upon a NULL
* parameter check. */
if( sntpStatus != SntpSuccess )
{
pContext = NULL;
}
return pContext;
}

View File

@ -0,0 +1,6 @@
CBMC proof stubs
======================
This directory contains the stubs written for CBMC proofs. It is
common to stub out functionality like network send and receive methods
when writing a CBMC proof, and the code for these stubs goes here.

View File

@ -0,0 +1,236 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_stubs.c
* @brief Definition of stubs for UDP transport and authentication interfaces of coreSNTP API.
*/
#include <stdint.h>
#include "core_sntp_client.h"
#include "core_sntp_serializer.h"
#include "core_sntp_stubs.h"
#define TEST_TIMESTAMP \
{ \
.seconds = UINT32_MAX, \
.fractions = 1000 \
}
/* An exclusive bound on the times that the NetworkInterfaceSendStub will be
* invoked before returning a loop terminating value. This is usually defined
* in the Makefile of the harnessed function. */
#ifndef MAX_NETWORK_SEND_TRIES
#define MAX_NETWORK_SEND_TRIES 2
#endif
/* An exclusive bound on the times that the NetworkInterfaceReceiveStub will
* return an unbound value. At this value and beyond, the
* NetworkInterfaceReceiveStub will return zero on every call. */
#ifndef MAX_NETWORK_RECV_TRIES
#define MAX_NETWORK_RECV_TRIES 5
#endif
static SntpTimestamp_t testTime = TEST_TIMESTAMP;
int32_t NetworkInterfaceReceiveStub( NetworkContext_t * pNetworkContext,
uint32_t serverAddr,
uint16_t serverPort,
void * pBuffer,
uint16_t bytesToRecv )
{
__CPROVER_assert( pBuffer != NULL,
"NetworkInterfaceReceiveStub pBuffer is NULL." );
__CPROVER_assert( __CPROVER_w_ok( pBuffer, bytesToRecv ),
"NetworkInterfaceReceiveStub pBuffer is not writable up to bytesToRecv." );
/* The havoc fills the buffer with unconstrained values. */
__CPROVER_havoc_object( pBuffer );
int32_t bytesOrError;
static size_t tries = 0;
/* It is a bug for the application defined transport receive function to return
* more than bytesToRecv. */
__CPROVER_assume( bytesOrError <= ( int32_t ) bytesToRecv );
if( tries < ( MAX_NETWORK_RECV_TRIES - 1 ) )
{
tries++;
}
else
{
tries = 0;
bytesOrError = SNTP_PACKET_BASE_SIZE;
}
return bytesOrError;
}
int32_t NetworkInterfaceSendStub( NetworkContext_t * pNetworkContext,
uint32_t serverAddr,
uint16_t serverPort,
const void * pBuffer,
uint16_t bytesToSend )
{
__CPROVER_assert( pBuffer != NULL,
"NetworkInterfaceSendStub pBuffer is NULL." );
__CPROVER_assert( __CPROVER_r_ok( pBuffer, bytesToSend ),
"NetworkInterfaceSendStub pBuffer is not readable up to bytesToSend." );
/* The number of tries to send the message before this invocation. */
static size_t tries = 0;
int32_t bytesOrError;
/* It is a bug for the application defined transport send function to return
* more than bytesToSend. */
__CPROVER_assume( bytesOrError <= ( int32_t ) bytesToSend );
/* If the maximum tries are reached, then return a timeout. In the SNTP library
* this stub is wrapped in a loop that will not end until the bytesOrError
* returned is negative. This means we could loop possibly INT32_MAX
* iterations. Looping for INT32_MAX times adds no value to the proof.
* What matters is that the SNTP library can handle all the possible values
* that could be returned. */
if( tries < ( MAX_NETWORK_SEND_TRIES - 1 ) )
{
tries++;
}
else
{
tries = 0;
/* This ensures that all the remaining bytes are sent in the last try. */
bytesOrError = bytesToSend;
}
return bytesOrError;
}
SntpStatus_t GenerateClientAuthStub( SntpAuthContext_t * pContext,
const SntpServerInfo_t * pTimeServer,
void * pBuffer,
size_t bufferSize,
uint16_t * pAuthCodeSize )
{
__CPROVER_assert( pTimeServer != NULL,
"GenerateClientAuthStub Time Server is NULL." );
__CPROVER_assert( pBuffer != NULL,
"GenerateClientAuthStub pBuffer is NULL." );
SntpStatus_t sntpStatus = SntpSuccess;
if( bufferSize <= SNTP_PACKET_BASE_SIZE )
{
sntpStatus = SntpErrorBufferTooSmall;
}
else
{
*pAuthCodeSize = SNTP_PACKET_BASE_SIZE;
}
return sntpStatus;
}
SntpStatus_t ValidateServerAuthStub( SntpAuthContext_t * pContext,
const SntpServerInfo_t * pTimeServer,
const void * pResponseData,
uint16_t responseSize )
{
return SntpSuccess;
}
bool ResolveDnsFuncStub( const SntpServerInfo_t * pServerAddr,
uint32_t * pIpV4Addr )
{
__CPROVER_assert( pServerAddr != NULL,
"ResolveDnsFuncStub pServerAddr is NULL." );
/* For the proofs, returning a non deterministic boolean value
* will be good enough. */
return nondet_bool();
}
void GetTimeFuncStub( SntpTimestamp_t * pCurrentTime )
{
__CPROVER_assert( pCurrentTime != NULL,
"GetTimeFuncStub pCurrentTime is NULL." );
bool value = nondet_bool();
if( value )
{
testTime.fractions = testTime.fractions + ( uint32_t ) 100000000;
}
else
{
testTime.fractions = testTime.fractions - ( uint32_t ) 1;
}
*pCurrentTime = testTime;
}
void SetTimeFuncStub( const SntpServerInfo_t * pTimeServer,
const SntpTimestamp_t * pServerTime,
int64_t clockOffsetMs )
{
__CPROVER_assert( pTimeServer != NULL,
"SetTimeFuncStub pTimeServer is NULL." );
__CPROVER_assert( pServerTime != NULL,
"SetTimeFuncStub pServerTime is NULL." );
}
SntpStatus_t Sntp_SerializeRequest( SntpTimestamp_t * pRequestTime,
uint32_t randomNumber,
void * pBuffer,
size_t bufferSize )
{
__CPROVER_assert( pRequestTime != NULL,
"Sntp_SerializeRequest pRequestTime is NULL." );
__CPROVER_assert( pBuffer != NULL,
"Sntp_SerializeRequest pBuffer is NULL." );
return SntpSuccess;
}
SntpStatus_t Sntp_DeserializeResponse( const SntpTimestamp_t * pRequestTime,
const SntpTimestamp_t * pResponseRxTime,
const void * pResponseBuffer,
size_t bufferSize,
SntpResponseData_t * pParsedResponse )
{
if( nondet_bool() )
{
return SntpSuccess;
}
else
{
return SntpRejectedResponseRetryWithBackoff;
}
}

View File

@ -0,0 +1,103 @@
# Include file path configuration for coreSNTP library.
include(${MODULE_ROOT_DIR}/coreSntpFilePaths.cmake)
project ("coreSNTP unit tests")
cmake_minimum_required (VERSION 3.2.0)
# ==================== Define your project name ========================
set(project_name "core_sntp")
# ===================== Create your mock here ========================
# list the files to mock here
list(APPEND mock_list
"${MODULE_ROOT_DIR}/source/include/core_sntp_serializer.h"
)
# list the directories your mocks need
list(APPEND mock_include_list
${CORE_SNTP_INCLUDE_PUBLIC_DIRS}
)
#list the definitions of your mocks to control what to be included
list(APPEND mock_define_list
""
)
# ================= Create the library under test here ==================
# list the files you would like to test here
list(APPEND real_source_files
${CORE_SNTP_SOURCES}
)
# list the directories the module under test includes
list(APPEND real_include_directories
${CORE_SNTP_INCLUDE_PUBLIC_DIRS}
${CMAKE_CURRENT_LIST_DIR}
)
# ===================== Create UnitTest Code here =====================
# list the directories your test needs to include
list(APPEND test_include_directories
${CORE_SNTP_INCLUDE_PUBLIC_DIRS}
${CMAKE_CURRENT_LIST_DIR}
)
# ============================= Create unit test targets ===================================
set(mock_name "${project_name}_mock")
set(real_name "${project_name}_real")
create_mock_list(${mock_name}
"${mock_list}"
"${MODULE_ROOT_DIR}/tools/cmock/project.yml"
"${mock_include_list}"
"${mock_define_list}"
)
create_real_library(${real_name}
"${real_source_files}"
"${real_include_directories}"
"${mock_name}"
)
# As both Mock and Real libraries targets contain the
# symbols for the core_sntp_serializer.c file, the linking
# order has the mock library first for the core_sntp_client_utest.c
# to use the mock for Serializer API calls.
list(APPEND utest_link_list
-l${mock_name}
lib${real_name}.a
)
list(APPEND utest_dep_list
${real_name}
)
# core_sntp_client_utest target
set(utest_name "${project_name}_client_utest")
set(utest_source "${project_name}_client_utest.c")
create_test(${utest_name}
${utest_source}
"${utest_link_list}"
"${utest_dep_list}"
"${test_include_directories}"
)
# Redefine the linking list as the mock is not needed for
# the core_sntp_serializer tests.
set(utest_link_list "")
list(APPEND utest_link_list
lib${real_name}.a
)
# core_sntp_serializer_utest target
set(utest_name "${project_name}_serializer_utest")
set(utest_source "${project_name}_serializer_utest.c")
create_test(${utest_name}
${utest_source}
"${utest_link_list}"
"${utest_dep_list}"
"${test_include_directories}"
)

View File

@ -0,0 +1,58 @@
# Macro utility to clone the CMock submodule.
macro( clone_cmock )
find_package( Git REQUIRED )
message( "Cloning submodule CMock." )
execute_process( COMMAND rm -rf ${CMOCK_DIR}
COMMAND ${GIT_EXECUTABLE} submodule update --checkout --init --recursive ${CMOCK_DIR}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE CMOCK_CLONE_RESULT )
if( NOT ${CMOCK_CLONE_RESULT} STREQUAL "0" )
message( FATAL_ERROR "Failed to clone CMock submodule." )
endif()
endmacro()
# Macro utility to add library targets for Unity and CMock to build configuration.
macro( add_cmock_targets )
# Build Configuration for CMock and Unity libraries.
list( APPEND CMOCK_INCLUDE_DIRS
"${CMOCK_DIR}/vendor/unity/src/"
"${CMOCK_DIR}/vendor/unity/extras/fixture/src"
"${CMOCK_DIR}/vendor/unity/extras/memory/src"
"${CMOCK_DIR}/src"
)
add_library(cmock STATIC
"${CMOCK_DIR}/src/cmock.c"
)
set_target_properties(cmock PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
POSITION_INDEPENDENT_CODE ON
COMPILE_FLAGS "-Og"
)
target_include_directories(cmock PUBLIC
${CMOCK_DIR}/src
${CMOCK_DIR}/vendor/unity/src/
${CMOCK_DIR}/examples
${CMOCK_INCLUDE_DIRS}
)
add_library(unity STATIC
"${CMOCK_DIR}/vendor/unity/src/unity.c"
"${CMOCK_DIR}/vendor/unity/extras/fixture/src/unity_fixture.c"
"${CMOCK_DIR}/vendor/unity/extras/memory/src/unity_memory.c"
)
set_target_properties(unity PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
POSITION_INDEPENDENT_CODE ON
)
target_include_directories(unity PUBLIC
${CMOCK_INCLUDE_DIRS}
)
target_link_libraries(cmock unity)
endmacro()

View File

@ -0,0 +1,62 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @file core_sntp_config.h
* @brief This header sets configuration macros for the SNTP library.
*/
#ifndef CORE_SNTP_CONFIG_H_
#define CORE_SNTP_CONFIG_H_
/* Standard include. */
#include <stdio.h>
/* @[code_example_loggingmacros] */
/************* Define Logging Macros using printf function ***********/
#define PrintfError( ... ) printf( "Error: "__VA_ARGS__ ); printf( "\n" )
#define PrintfWarn( ... ) printf( "Warn: "__VA_ARGS__ ); printf( "\n" )
#define PrintfInfo( ... ) printf( "Info: " __VA_ARGS__ ); printf( "\n" )
#define PrintfDebug( ... ) printf( "Debug: " __VA_ARGS__ ); printf( "\n" )
#ifdef LOGGING_LEVEL_ERROR
#define LogError( message ) PrintfError message
#elif defined( LOGGING_LEVEL_WARNING )
#define LogError( message ) PrintfError message
#define LogWarn( message ) PrintfWarn message
#elif defined( LOGGING_LEVEL_INFO )
#define LogError( message ) PrintfError message
#define LogWarn( message ) PrintfWarn message
#define LogInfo( message ) PrintfInfo message
#elif defined( LOGGING_LEVEL_DEBUG )
#define LogError( message ) PrintfError message
#define LogWarn( message ) PrintfWarn message
#define LogInfo( message ) PrintfInfo message
#define LogDebug( message ) PrintfDebug message
#endif /* ifdef LOGGING_LEVEL_ERROR */
/**************************************************/
/* @[code_example_loggingmacros] */
#endif /* ifndef CORE_SNTP_CONFIG_H_ */

View File

@ -0,0 +1,967 @@
/*
* coreSNTP v1.2.0
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Standard includes. */
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
/* POSIX include. */
#include <arpa/inet.h>
/* Unity include. */
#include "unity.h"
/* coreSNTP Serializer API include */
#include "core_sntp_serializer.h"
#define TEST_TIMESTAMP \
{ \
.seconds = UINT32_MAX, \
.fractions = 1000 \
}
#define ZERO_TIMESTAMP \
{ \
.seconds = 0, \
.fractions = 0 \
}
/* Bits 3-5 are used for Version in 1st byte of SNTP packet. */
#define SNTP_PACKET_VERSION_VAL ( 4 /* Version */ << 3 /* Bits 3-5 used in byte */ )
/* Values for "Mode" field in an SNTP packet. */
#define SNTP_PACKET_MODE_SERVER ( 4 )
#define SNTP_PACKET_MODE_CLIENT ( 3 )
/* The least significant bit position of "Leap Indicator" field
* in the first byte of an SNTP packet. */
#define SNTP_PACKET_LEAP_INDICATOR_LSB ( 6 )
/* The byte positions of SNTP packet fields in the 48 bytes sized
* packet format. */
#define SNTP_PACKET_STRATUM_BYTE_POS ( 1 )
#define SNTP_PACKET_KOD_CODE_FIRST_BYTE_POS ( 12 )
#define SNTP_PACKET_ORIGIN_TIME_FIRST_BYTE_POS ( 24 )
#define SNTP_PACKET_RX_TIMESTAMP_FIRST_BYTE_POS ( 32 )
#define SNTP_PACKET_TX_TIMESTAMP_FIRST_BYTE_POS ( 40 )
/* Values of "Stratum" field in an SNTP packet. */
#define SNTP_PACKET_STRATUM_KOD ( 0 )
#define SNTP_PACKET_STRATUM_SECONDARY_SERVER ( 15 )
/* ASCII string codes that a server can send in a Kiss-o'-Death response. */
#define KOD_CODE_DENY "DENY"
#define KOD_CODE_RSTR "RSTR"
#define KOD_CODE_RATE "RATE"
#define KOD_CODE_OTHER_EXAMPLE_1 "AUTH"
#define KOD_CODE_OTHER_EXAMPLE_2 "CRYP"
#define YEARS_20_IN_SECONDS ( ( 20 * 365 + 20 / 4 ) * 24 * 3600 )
#define YEARS_68_IN_SECONDS ( ( 68 * 365 + 68 / 4 ) * 24 * 3600 )
/* Macro utility to convert the fixed-size Kiss-o'-Death ASCII code
* to integer.*/
#define INTEGER_VAL_OF_KOD_CODE( codePtr ) \
( ( uint32_t ) ( ( ( uint32_t ) codePtr[ 0 ] << 24 ) | \
( ( uint32_t ) codePtr[ 1 ] << 16 ) | \
( ( uint32_t ) codePtr[ 2 ] << 8 ) | \
( ( uint32_t ) codePtr[ 3 ] ) ) )
/* Buffer used for SNTP requests and responses in tests. */
static uint8_t testBuffer[ SNTP_PACKET_BASE_SIZE ];
static SntpResponseData_t parsedData;
static SntpTimestamp_t zeroTimestamp = ZERO_TIMESTAMP;
/* ============================ Helper Functions ============================ */
/* Utility macro to convert seconds to milliseconds. */
static uint64_t TO_MS( uint64_t seconds )
{
return( seconds * 1000 );
}
static void addTimestampToResponseBuffer( SntpTimestamp_t * pTime,
uint8_t * pResponseBuffer,
size_t startingPos )
{
/* Convert the request time into network byte order to use to fill in buffer. */
uint32_t secs = pTime->seconds;
uint32_t fracs = pTime->fractions;
pResponseBuffer[ startingPos ] = secs >> 24; /* seconds, byte 1*/
pResponseBuffer[ startingPos + 1 ] = secs >> 16; /* seconds, byte 2 */
pResponseBuffer[ startingPos + 2 ] = secs >> 8; /* seconds, byte 3 */
pResponseBuffer[ startingPos + 3 ] = secs; /* seconds, byte 4 */
pResponseBuffer[ startingPos + 4 ] = fracs >> 24; /* fractions, byte 1*/
pResponseBuffer[ startingPos + 5 ] = fracs >> 16; /* fractions, byte 2 */
pResponseBuffer[ startingPos + 6 ] = fracs >> 8; /* fractions, byte 3 */
pResponseBuffer[ startingPos + 7 ] = fracs; /* fractions, byte 4 */
}
static void fillValidSntpResponseData( uint8_t * pBuffer,
SntpTimestamp_t * pRequestTime )
{
/* Clear the buffer. */
memset( pBuffer, 0, SNTP_PACKET_BASE_SIZE );
/* Set the "Version" and "Mode" fields in the first byte of SNTP packet. */
pBuffer[ 0 ] = SNTP_PACKET_VERSION_VAL | SNTP_PACKET_MODE_SERVER;
/* Set the SNTP response packet to contain the "originate" timestamp
* correctly, as matching the SNTP request timestamp. */
addTimestampToResponseBuffer( pRequestTime,
pBuffer,
SNTP_PACKET_ORIGIN_TIME_FIRST_BYTE_POS );
SntpTimestamp_t testTime = TEST_TIMESTAMP;
/* Set the SNTP response packet to contain the "receive" timestamp
* correctly, as matching the SNTP request timestamp. */
addTimestampToResponseBuffer( &testTime,
pBuffer,
SNTP_PACKET_RX_TIMESTAMP_FIRST_BYTE_POS );
/* Set the SNTP response packet to contain the "transmit" timestamp
* correctly, as matching the SNTP request timestamp. */
addTimestampToResponseBuffer( &testTime,
pBuffer,
SNTP_PACKET_TX_TIMESTAMP_FIRST_BYTE_POS );
/* Set the "Stratum" byte in the response packet to represent a
* secondary NTP server. */
pBuffer[ SNTP_PACKET_STRATUM_BYTE_POS ] = SNTP_PACKET_STRATUM_SECONDARY_SERVER;
}
/* Common test code that fills SNTP response packet with server times and validates
* that @ref Sntp_DeserializeResponse API correctly calculates the clock offset. */
static void testClockOffsetCalculation( SntpTimestamp_t * clientTxTime,
SntpTimestamp_t * serverRxTime,
SntpTimestamp_t * serverTxTime,
SntpTimestamp_t * clientRxTime,
SntpStatus_t expectedStatus,
int64_t expectedClockOffsetMs )
{
/* Update the response packet with the server time. */
addTimestampToResponseBuffer( clientTxTime,
testBuffer,
SNTP_PACKET_ORIGIN_TIME_FIRST_BYTE_POS );
addTimestampToResponseBuffer( serverRxTime,
testBuffer,
SNTP_PACKET_RX_TIMESTAMP_FIRST_BYTE_POS );
addTimestampToResponseBuffer( serverTxTime,
testBuffer,
SNTP_PACKET_TX_TIMESTAMP_FIRST_BYTE_POS );
/* Call the API under test. */
TEST_ASSERT_EQUAL( expectedStatus, Sntp_DeserializeResponse( clientTxTime,
clientRxTime,
testBuffer,
sizeof( testBuffer ),
&parsedData ) );
/* Make sure that the API has indicated in the output parameter that
* clock-offset could not be calculated. */
TEST_ASSERT_EQUAL( expectedClockOffsetMs, parsedData.clockOffsetMs );
/* Validate other fields in the output parameter. */
TEST_ASSERT_EQUAL( 0, memcmp( &parsedData.serverTime, serverTxTime, sizeof( SntpTimestamp_t ) ) );
TEST_ASSERT_EQUAL( NoLeapSecond, parsedData.leapSecondType );
TEST_ASSERT_EQUAL( SNTP_KISS_OF_DEATH_CODE_NONE, parsedData.rejectedResponseCode );
}
/* ============================ UNITY FIXTURES ============================ */
/* Called before each test method. */
void setUp()
{
memset( &parsedData, 0, sizeof( parsedData ) );
}
/* Called at the beginning of the whole suite. */
void suiteSetUp()
{
}
/* Called at the end of the whole suite. */
int suiteTearDown( int numFailures )
{
return numFailures;
}
/* ========================================================================== */
/**
* @brief Test @ref Sntp_SerializeRequest with invalid parameters.
*/
void test_SerializeRequest_InvalidParams( void )
{
SntpTimestamp_t testTime = TEST_TIMESTAMP;
/* Pass invalid time object. */
TEST_ASSERT_EQUAL( SntpErrorBadParameter,
Sntp_SerializeRequest( NULL,
( rand() % UINT32_MAX ),
testBuffer,
sizeof( testBuffer ) ) );
/* Pass invalid buffer. */
TEST_ASSERT_EQUAL( SntpErrorBadParameter,
Sntp_SerializeRequest( &testTime,
( rand() % UINT32_MAX ),
NULL,
sizeof( testBuffer ) ) );
/* Pass zero timestamp for request time. */
TEST_ASSERT_EQUAL( SntpErrorBadParameter,
Sntp_SerializeRequest( &zeroTimestamp,
( rand() % UINT32_MAX ),
testBuffer,
sizeof( testBuffer ) ) );
/* Pass a buffer size less than 48 bytes of minimum SNTP packet size. */
TEST_ASSERT_EQUAL( SntpErrorBufferTooSmall,
Sntp_SerializeRequest( &testTime,
( rand() % UINT32_MAX ),
testBuffer,
1 ) );
}
/**
* @brief Validate the serialization operation of the @ref Sntp_SerializeRequest API.
*/
void test_SerializeRequest_NominalCase( void )
{
SntpTimestamp_t testTime = TEST_TIMESTAMP;
const uint32_t randomVal = 0xAABBCCDD;
/* Expected transmit timestamp in the SNTP request packet. */
const SntpTimestamp_t expectedTxTime =
{
.seconds = testTime.seconds,
.fractions = ( testTime.fractions | ( randomVal >> 16 ) )
};
/* The expected serialization of the SNTP request packet. */
uint8_t expectedSerialization[ SNTP_PACKET_BASE_SIZE ] =
{
0x00 /* Leap Indicator */ | 0x20 /* Version */ | 0x03, /* Client Mode */
0x00, /* stratum */
0x00, /* poll interval */
0x00, /* precision */
0x00, 0x00, 0x00, 0x00, /* root delay */
0x00, 0x00, 0x00, 0x00, /* root dispersion */
0x00, 0x00, 0x00, 0x00, /* reference ID */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reference time */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* origin timestamp */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* receive timestamp */
expectedTxTime.seconds >> 24, /* transmit timestamp - seconds, byte 1 */
expectedTxTime.seconds >> 16, /* transmit timestamp - seconds, byte 2 */
expectedTxTime.seconds >> 8, /* transmit timestamp - seconds, byte 3 */
expectedTxTime.seconds, /* transmit timestamp - seconds, byte 4 */
expectedTxTime.fractions >> 24, /* transmit timestamp - fractions, byte 1 */
expectedTxTime.fractions >> 16, /* transmit timestamp - fractions, byte 2 */
expectedTxTime.fractions >> 8, /* transmit timestamp - fractions, byte 3 */
expectedTxTime.fractions, /* transmit timestamp - fractions, byte 4 */
};
/* Call the API under test. */
TEST_ASSERT_EQUAL( SntpSuccess,
Sntp_SerializeRequest( &testTime,
randomVal,
testBuffer,
sizeof( testBuffer ) ) );
/* Validate that serialization operation by the API. */
TEST_ASSERT_EQUAL_UINT8_ARRAY( expectedSerialization,
testBuffer,
SNTP_PACKET_BASE_SIZE );
/* Check that the request timestamp object has been updated with the random value. */
TEST_ASSERT_EQUAL( 0, memcmp( &expectedTxTime,
&testTime,
sizeof( SntpTimestamp_t ) ) );
}
/**
* @brief Test @ref Sntp_DeserializeResponse with invalid parameters.
*/
void test_DeserializeResponse_InvalidParams( void )
{
SntpTimestamp_t testTime = TEST_TIMESTAMP;
/* Pass invalid time objects. */
TEST_ASSERT_EQUAL( SntpErrorBadParameter,
Sntp_DeserializeResponse( NULL,
&testTime,
testBuffer,
sizeof( testBuffer ),
&parsedData ) );
TEST_ASSERT_EQUAL( SntpErrorBadParameter,
Sntp_DeserializeResponse( &testTime,
NULL,
testBuffer,
sizeof( testBuffer ),
&parsedData ) );
/* Pass invalid buffer. */
TEST_ASSERT_EQUAL( SntpErrorBadParameter,
Sntp_DeserializeResponse( &testTime,
&testTime,
NULL,
sizeof( testBuffer ),
&parsedData ) );
/* Pass a buffer size less than 48 bytes of minimum SNTP packet size. */
TEST_ASSERT_EQUAL( SntpErrorBufferTooSmall,
Sntp_DeserializeResponse( &testTime,
&testTime,
testBuffer,
sizeof( testBuffer ) / 2,
&parsedData ) );
/* Pass invalid output parameter. */
TEST_ASSERT_EQUAL( SntpErrorBadParameter,
Sntp_DeserializeResponse( &testTime,
&testTime,
testBuffer,
sizeof( testBuffer ),
NULL ) );
/* Pass zero timestamp for request time. */
TEST_ASSERT_EQUAL( SntpErrorBadParameter,
Sntp_DeserializeResponse( &zeroTimestamp,
&testTime,
testBuffer,
sizeof( testBuffer ),
&parsedData ) );
}
/**
* @brief Test that @ref Sntp_DeserializeResponse API can detect invalid
* SNTP response packets.
*/
void test_DeserializeResponse_Invalid_Responses( void )
{
SntpTimestamp_t clientTime = TEST_TIMESTAMP;
/* Fill buffer with general SNTP response data. */
fillValidSntpResponseData( testBuffer, &clientTime );
/* ******* Test when SNTP packet does not a non-server value in the "Mode" field. **** */
testBuffer[ 0 ] = SNTP_PACKET_VERSION_VAL | SNTP_PACKET_MODE_CLIENT;
/* Call the API under test. */
TEST_ASSERT_EQUAL( SntpInvalidResponse, Sntp_DeserializeResponse( &clientTime,
&clientTime,
testBuffer,
sizeof( testBuffer ),
&parsedData ) );
/* Set the Mode field to the correct value for Server. */
testBuffer[ 0 ] = SNTP_PACKET_VERSION_VAL | SNTP_PACKET_MODE_SERVER;
/************** Test when "originate timestamp" is zero ***************/
addTimestampToResponseBuffer( &zeroTimestamp,
testBuffer,
SNTP_PACKET_ORIGIN_TIME_FIRST_BYTE_POS );
/* Call the API under test. */
TEST_ASSERT_EQUAL( SntpInvalidResponse, Sntp_DeserializeResponse( &clientTime,
&clientTime,
testBuffer,
sizeof( testBuffer ),
&parsedData ) );
/* Set the "originate timestamp" to a non-zero value for the next test. */
addTimestampToResponseBuffer( &clientTime,
testBuffer,
SNTP_PACKET_ORIGIN_TIME_FIRST_BYTE_POS );
/************** Test when "receive timestamp" is zero ***************/
addTimestampToResponseBuffer( &zeroTimestamp,
testBuffer,
SNTP_PACKET_RX_TIMESTAMP_FIRST_BYTE_POS );
/* Call the API under test. */
TEST_ASSERT_EQUAL( SntpInvalidResponse, Sntp_DeserializeResponse( &clientTime,
&clientTime,
testBuffer,
sizeof( testBuffer ),
&parsedData ) );
/* Set the "receive timestamp" to a non-zero value for the next test. */
addTimestampToResponseBuffer( &clientTime,
testBuffer,
SNTP_PACKET_RX_TIMESTAMP_FIRST_BYTE_POS );
/************** Test when "transmit timestamp" is zero ***************/
addTimestampToResponseBuffer( &zeroTimestamp,
testBuffer,
SNTP_PACKET_TX_TIMESTAMP_FIRST_BYTE_POS );
/* Call the API under test. */
TEST_ASSERT_EQUAL( SntpInvalidResponse, Sntp_DeserializeResponse( &clientTime,
&clientTime,
testBuffer,
sizeof( testBuffer ),
&parsedData ) );
/* Set the "transmit timestamp" to a non-zero value for the next test. */
addTimestampToResponseBuffer( &clientTime,
testBuffer,
SNTP_PACKET_TX_TIMESTAMP_FIRST_BYTE_POS );
/******** Test when SNTP response packet does not have "originate" timestamp matching
* the "transmit" time sent by the client in the SNTP request. ************/
SntpTimestamp_t originateTime = TEST_TIMESTAMP;
/* Test when only the seconds part of the "originate" timestamp does not match
* the client request time .*/
originateTime.seconds = clientTime.seconds + 1;
addTimestampToResponseBuffer( &originateTime,
testBuffer,
SNTP_PACKET_ORIGIN_TIME_FIRST_BYTE_POS );
/* Call the API under test. */
TEST_ASSERT_EQUAL( SntpInvalidResponse, Sntp_DeserializeResponse( &clientTime,
&clientTime,
testBuffer,
sizeof( testBuffer ),
&parsedData ) );
/* Test when only the fractions part of the "originate" timestamp does not match
* the client request time .*/
originateTime.seconds = clientTime.seconds;
originateTime.fractions = clientTime.fractions + 1;
addTimestampToResponseBuffer( &originateTime,
testBuffer,
SNTP_PACKET_ORIGIN_TIME_FIRST_BYTE_POS );
/* Call the API under test. */
TEST_ASSERT_EQUAL( SntpInvalidResponse, Sntp_DeserializeResponse( &clientTime,
&clientTime,
testBuffer,
sizeof( testBuffer ),
&parsedData ) );
}
/**
* @brief Test @ref Sntp_DeserializeResponse API to de-serialize Kiss-o'-Death
* responses from SNTP server.
*
* The API should return an error code appropriate for the Kiss-o'-Death code
* and update the member of output parameter to point the ASCII string code
* in the response packet.
*/
void test_DeserializeResponse_KoD_packets( void )
{
/* Use same value for request and response times, as API should not process
* them for Kiss-o'-Death response packets. */
SntpTimestamp_t testTime = TEST_TIMESTAMP;
uint32_t KodCodeNetworkOrder = 0;
/* Populate the buffer with a valid SNTP response before converting it
* into a Kiss-o'-Death message. */
fillValidSntpResponseData( testBuffer, &testTime );
/* Update the "Stratum" field in the buffer to make the packet a Kiss-o'-Death message. */
testBuffer[ SNTP_PACKET_STRATUM_BYTE_POS ] = SNTP_PACKET_STRATUM_KOD;
/* Common test code for testing de-serialization of Kiss-o'-Death packet containing a specific
* code with@ref Sntp_DeserializeResponse API. */
#define TEST_API_FOR_KOD_CODE( code, expectedStatus ) \
do { \
KodCodeNetworkOrder = INTEGER_VAL_OF_KOD_CODE( code ); \
testBuffer[ SNTP_PACKET_KOD_CODE_FIRST_BYTE_POS ] = KodCodeNetworkOrder >> 24; \
testBuffer[ SNTP_PACKET_KOD_CODE_FIRST_BYTE_POS + 1 ] = KodCodeNetworkOrder >> 16; \
testBuffer[ SNTP_PACKET_KOD_CODE_FIRST_BYTE_POS + 2 ] = KodCodeNetworkOrder >> 8; \
testBuffer[ SNTP_PACKET_KOD_CODE_FIRST_BYTE_POS + 3 ] = KodCodeNetworkOrder; \
\
/* Call API under test. */ \
TEST_ASSERT_EQUAL( expectedStatus, \
Sntp_DeserializeResponse( &testTime, \
&testTime, \
testBuffer, \
sizeof( testBuffer ), \
&parsedData ) ); \
\
/* Test that API has populated the output parameter with the parsed \
* KoD code. */ \
TEST_ASSERT_EQUAL( INTEGER_VAL_OF_KOD_CODE( code ), \
parsedData.rejectedResponseCode ); \
\
} while( 0 )
/* Test Kiss-o'-Death server response with "DENY" code. */
TEST_API_FOR_KOD_CODE( KOD_CODE_DENY, SntpRejectedResponseChangeServer );
/* Test Kiss-o'-Death server response with "RSTR" code. */
TEST_API_FOR_KOD_CODE( KOD_CODE_RSTR, SntpRejectedResponseChangeServer );
/* Test Kiss-o'-Death server response with "RATE" code. */
TEST_API_FOR_KOD_CODE( KOD_CODE_RATE, SntpRejectedResponseRetryWithBackoff );
/* ***** Test de-serialization of Kiss-o'-Death server response with other codes ***** */
TEST_API_FOR_KOD_CODE( KOD_CODE_OTHER_EXAMPLE_1, SntpRejectedResponseOtherCode );
TEST_API_FOR_KOD_CODE( KOD_CODE_OTHER_EXAMPLE_2, SntpRejectedResponseOtherCode );
}
/**
* @brief Test that @ref Sntp_DeserializeResponse API can process an accepted
* SNTP server response, and compute clock-offset when the server and client times
* are >= 68 years apart.
*/
void test_DeserializeResponse_AcceptedResponse_ClockOffset_Edge_Cases( void )
{
SntpTimestamp_t clientTime = TEST_TIMESTAMP;
/* Fill buffer with general SNTP response data. */
fillValidSntpResponseData( testBuffer, &clientTime );
/* Test when the client is 68 years ahead of server time .*/
SntpTimestamp_t serverTime =
{
clientTime.seconds - YEARS_68_IN_SECONDS,
clientTime.fractions
};
testClockOffsetCalculation( &clientTime, &serverTime,
&serverTime, &clientTime,
SntpSuccess,
TO_MS( -YEARS_68_IN_SECONDS ) );
/* Now test when the client is 68 years ahead of server time .*/
serverTime.seconds = clientTime.seconds + YEARS_68_IN_SECONDS;
testClockOffsetCalculation( &clientTime, &serverTime,
&serverTime, &clientTime,
SntpSuccess,
TO_MS( YEARS_68_IN_SECONDS ) );
/* Now test special cases when the server and client times are (INT32_MAX + 1) =
* 2^31 apart seconds apart. The library should ALWAYS think that server
* is ahead of the client in this special case, and thus return the maximum
* signed 32 bit integer as the clock-offset.
*/
/* Case when "Server Time - Client Time" results in negative value. */
serverTime.seconds = clientTime.seconds + INT32_MAX + 1;
testClockOffsetCalculation( &clientTime, &serverTime,
&serverTime, &clientTime,
SntpSuccess,
TO_MS( ( int64_t ) INT32_MAX + 1 ) );
/* Test when "Server Time - Client Time" results in positive value. */
serverTime.seconds = UINT32_MAX;
clientTime.seconds = serverTime.seconds - INT32_MAX - 1;
testClockOffsetCalculation( &clientTime, &serverTime,
&serverTime, &clientTime,
SntpSuccess,
TO_MS( ( int64_t ) INT32_MAX + 1 ) );
/* Reset client time to UINT32_MAX */
clientTime.seconds = UINT32_MAX;
/* Now test cases when the server and client times are exactly
* INT32_MAX seconds apart. */
serverTime.seconds = clientTime.seconds - INT32_MAX;
testClockOffsetCalculation( &clientTime, &serverTime,
&serverTime, &clientTime,
SntpSuccess,
TO_MS( -INT32_MAX ) );
serverTime.seconds = clientTime.seconds + INT32_MAX;
testClockOffsetCalculation( &clientTime, &serverTime,
&serverTime, &clientTime,
SntpSuccess,
TO_MS( INT32_MAX ) );
/* Reset client and server times to be 68 years apart. */
clientTime.seconds = UINT32_MAX;
serverTime.seconds = UINT32_MAX + YEARS_68_IN_SECONDS;
/* Now test the contrived case when only the send network path
* represents timestamps that overflow but the receive network path
* has timestamps that do not overflow.
* As only the single network path contains time difference of 68 years,
* the expected clock-offset, being an average of the network paths, is
* 34 years of duration. */
testClockOffsetCalculation( &clientTime, &serverTime, /* Send Path times are 68 years apart */
&serverTime, &serverTime, /* Receive path times are the same. */
SntpSuccess,
TO_MS( YEARS_68_IN_SECONDS / 2 ) ); /* Expected offset of 34 years. */
/* Now test the contrived case when only the receive network path
* represents timestamps that overflow but the send network path
* has timestamps that do not overflow.
* As only the single network path contains time difference of 68 years,
* the expected clock-offset, being an average of the network paths, is
* 34 years of duration. */
testClockOffsetCalculation( &clientTime, &clientTime, /* Send Path times are the same, i.e. don't overflow */
&serverTime, &clientTime, /* Receive path times are 68 years apart. */
SntpSuccess,
TO_MS( YEARS_68_IN_SECONDS / 2 ) ); /* Expected offset of 34 years. */
}
void test_Sntp_DeserializeResponse_ClockOffset_Milliseconds_Cases( void )
{
SntpTimestamp_t clientTime =
{
.seconds = 0,
/* 500 milliseconds. */
.fractions = 500 * 1000 * SNTP_FRACTION_VALUE_PER_MICROSECOND
};
/* Fill buffer with general SNTP response data. */
fillValidSntpResponseData( testBuffer, &clientTime );
SntpTimestamp_t serverTime =
{
.seconds = clientTime.seconds,
.fractions = clientTime.fractions
};
/* Test when server is ahead of client time. */
serverTime.fractions = 700 * 1000 * SNTP_FRACTION_VALUE_PER_MICROSECOND; /* 700 milliseconds. */
testClockOffsetCalculation( &clientTime, &serverTime, /* Send Path times are 68 years apart */
&serverTime, &clientTime, /* Receive path times are the same. */
SntpSuccess, 200 );
/* Test when server is behind client time. */
serverTime.fractions = 100 * 1000 * SNTP_FRACTION_VALUE_PER_MICROSECOND; /* 100 milliseconds. */
testClockOffsetCalculation( &clientTime, &serverTime, /* Send Path times are 68 years apart */
&serverTime, &clientTime, /* Receive path times are the same. */
SntpSuccess, -400 );
/* Test when complex case of NTP time overflow with client being ahead of server by 3900 milliseconds. */
serverTime.seconds = UINT32_MAX - 3; /* 4 seconds behind in "seconds" value. */
serverTime.fractions = 600 * 1000 * SNTP_FRACTION_VALUE_PER_MICROSECOND; /* 100 milliseconds ahead. */
testClockOffsetCalculation( &clientTime, &serverTime, /* Send Path times are 68 years apart */
&serverTime, &clientTime, /* Receive path times are the same. */
SntpSuccess, -3900 );
}
/**
* @brief Test that @ref Sntp_DeserializeResponse API can process an accepted
* SNTP server response, and calculate the clock offset for non-overflow cases
* (i.e. when the client and server times are within 34 years of each other).
*/
void test_DeserializeResponse_AcceptedResponse_Nominal_Case( void )
{
SntpTimestamp_t clientTxTime = TEST_TIMESTAMP;
/* Fill buffer with general SNTP response data. */
fillValidSntpResponseData( testBuffer, &clientTxTime );
/* Use the the same values for Rx and Tx times for server and client in the first couple
* of tests for simplicity. */
/* ==================Test when client and server are in same NTP era.================ */
/* Test when the client is 20 years ahead of server time to generate a negative offset
* result.*/
SntpTimestamp_t serverTxTime =
{
clientTxTime.seconds - YEARS_20_IN_SECONDS,
clientTxTime.fractions
};
int32_t expectedOffset = -YEARS_20_IN_SECONDS;
testClockOffsetCalculation( &clientTxTime, &serverTxTime,
&serverTxTime, &clientTxTime,
SntpSuccess, TO_MS( expectedOffset ) );
/* Now test for the client being 20 years behind server time to generate a positive
* offset result.*/
serverTxTime.seconds = UINT32_MAX;
clientTxTime.seconds = UINT32_MAX - YEARS_20_IN_SECONDS;
expectedOffset = YEARS_20_IN_SECONDS;
testClockOffsetCalculation( &clientTxTime, &serverTxTime,
&serverTxTime, &clientTxTime,
SntpSuccess, TO_MS( expectedOffset ) );
/* ==================Test when client and server are in different NTP eras.================ */
/* Test when the server is ahead of client to generate a positive clock offset result.*/
clientTxTime.seconds = UINT32_MAX; /* Client is in NTP era 0. */
serverTxTime.seconds = UINT32_MAX + YEARS_20_IN_SECONDS; /* Server is in NTP era 1. */
expectedOffset = YEARS_20_IN_SECONDS;
testClockOffsetCalculation( &clientTxTime, &serverTxTime,
&serverTxTime, &clientTxTime,
SntpSuccess, TO_MS( expectedOffset ) );
/* Test when the client is ahead of server to generate a negative clock offset result.*/
clientTxTime.seconds = UINT32_MAX + YEARS_20_IN_SECONDS; /* Client is in NTP era 1. */
serverTxTime.seconds = UINT32_MAX; /* Server is in NTP era 0. */
expectedOffset = -YEARS_20_IN_SECONDS;
testClockOffsetCalculation( &clientTxTime, &serverTxTime,
&serverTxTime, &clientTxTime,
SntpSuccess, TO_MS( expectedOffset ) );
/* Now test with different values for T1 (client Tx), T2 (server Rx), T3 (server Tx) and T4 (client Rx)
* that are used in the clock-offset calculation.
* The test case uses 2 seconds as network delay on both Client -> Server and Server -> Client path
* and 2 seconds for server processing time between receiving SNTP request and sending SNTP response */
clientTxTime.seconds = UINT32_MAX;
SntpTimestamp_t serverRxTime =
{
clientTxTime.seconds + YEARS_20_IN_SECONDS + 2,
serverTxTime.fractions
};
serverTxTime.seconds = serverRxTime.seconds + 2;
SntpTimestamp_t clientRxTime =
{
clientTxTime.seconds + 6, /* 2 seconds each for Client -> Server, Server -> Server,
* Server -> Client */
clientTxTime.fractions
};
expectedOffset = YEARS_20_IN_SECONDS;
testClockOffsetCalculation( &clientTxTime, &serverRxTime,
&serverTxTime, &clientRxTime,
SntpSuccess, TO_MS( expectedOffset ) );
}
/**
* @brief Test that @ref Sntp_DeserializeResponse API can de-serialize leap-second
* information in an accepted SNTP response packet from a server.
*/
void test_DeserializeResponse_AcceptedResponse_LeapSecond( void )
{
SntpTimestamp_t clientTime = TEST_TIMESTAMP;
SntpTimestamp_t serverTime = TEST_TIMESTAMP;
/* Fill buffer with general SNTP response data. */
fillValidSntpResponseData( testBuffer, &clientTime );
/* Update the response packet with the server time. */
addTimestampToResponseBuffer( &serverTime,
testBuffer,
SNTP_PACKET_RX_TIMESTAMP_FIRST_BYTE_POS );
addTimestampToResponseBuffer( &serverTime,
testBuffer,
SNTP_PACKET_TX_TIMESTAMP_FIRST_BYTE_POS );
#define TEST_LEAP_SECOND_DESERIALIZATION( expectedLeapSecond ) \
do { \
/* Call the API under test. */ \
TEST_ASSERT_EQUAL( SntpSuccess, Sntp_DeserializeResponse( &clientTime, \
&clientTime, \
testBuffer, \
sizeof( testBuffer ), \
&parsedData ) ); \
\
/* As the clock and server times are same, the clock offset, should be zero. */ \
TEST_ASSERT_EQUAL( 0, parsedData.clockOffsetMs ); \
\
/* Validate other fields in the output parameter. */ \
TEST_ASSERT_EQUAL( 0, memcmp( &parsedData.serverTime, &serverTime, sizeof( SntpTimestamp_t ) ) ); \
TEST_ASSERT_EQUAL( expectedLeapSecond, parsedData.leapSecondType ); \
TEST_ASSERT_EQUAL( SNTP_KISS_OF_DEATH_CODE_NONE, parsedData.rejectedResponseCode ); \
} while( 0 )
/* Update SNTP response packet to indicate an upcoming leap second insertion. */
testBuffer[ 0 ] = ( LastMinuteHas61Seconds << SNTP_PACKET_LEAP_INDICATOR_LSB ) |
SNTP_PACKET_VERSION_VAL | SNTP_PACKET_MODE_SERVER;
TEST_LEAP_SECOND_DESERIALIZATION( LastMinuteHas61Seconds );
/* Update SNTP response packet to indicate an upcoming leap second deletion. */
testBuffer[ 0 ] = ( LastMinuteHas59Seconds << SNTP_PACKET_LEAP_INDICATOR_LSB ) |
SNTP_PACKET_VERSION_VAL | SNTP_PACKET_MODE_SERVER;
TEST_LEAP_SECOND_DESERIALIZATION( LastMinuteHas59Seconds );
}
/**
* @brief Tests the @ref Sntp_CalculatePollInterval utility function returns
* error for invalid parameters passed to the API.
*/
void test_CalculatePollInterval_InvalidParams( void )
{
uint32_t pollInterval = 0;
/* Test with invalid clock frequency. */
TEST_ASSERT_EQUAL( SntpErrorBadParameter, Sntp_CalculatePollInterval( 0,
100,
&pollInterval ) );
/* Test with invalid desired accuracy. */
TEST_ASSERT_EQUAL( SntpErrorBadParameter, Sntp_CalculatePollInterval( 100,
0,
&pollInterval ) );
/* Test with invalid output parameter. */
TEST_ASSERT_EQUAL( SntpErrorBadParameter, Sntp_CalculatePollInterval( 100,
50,
NULL ) );
/* Test with parameters that cause poll interval value of less than 1 second. */
TEST_ASSERT_EQUAL( SntpZeroPollInterval, Sntp_CalculatePollInterval( 10000 /* High Error Clock. */,
1 /* High Accuracy Requirement */,
&pollInterval ) );
}
/**
* @brief Tests the @ref Sntp_CalculatePollInterval utility function calculates
* the poll interval period as the closes power of 2 value for achieving the
* desired clock accuracy.
*/
void test_CalculatePollInterval_Nominal( void )
{
uint32_t pollInterval = 0;
uint32_t expectedInterval = 0;
/* Test the SNTPv4 specification example of 200 PPM clock frequency and
* 1 minute of desired accuracy. */
expectedInterval = 0x00040000;
TEST_ASSERT_EQUAL( SntpSuccess, Sntp_CalculatePollInterval( 200 /* Clock Tolerance */,
1 /* minute */ * 60 * 1000 /* Desired Accuracy */,
&pollInterval ) );
TEST_ASSERT_EQUAL( expectedInterval, pollInterval );
/* Test another case where the exact poll interval for achieving desired frequency
* is an exponent of 2 value. For 512 seconds (or 2^9) poll interval, the maximum
* clock drift with 125 PPM is 125 * 512 = 64000 microseconds = 64 milliseconds. */
expectedInterval = 0x00000200;
TEST_ASSERT_EQUAL( SntpSuccess, Sntp_CalculatePollInterval( 125 /* Clock Tolerance (PPM) */,
64 /* Desired Accuracy (ms)*/,
&pollInterval ) );
/* Test for maximum possible value of calculated poll interval when the clock frequency
* tolerance is minimum (i.e. 1 PPM or high accuracy system clock ) but the desired accuracy
* is very low (i.e. largest value of 16 bit parameter as 65535 ms OR ~ 1 minute).
* This test proves that the 32-bit
* width of poll integer value can hold the largest calculation of poll interval value. */
expectedInterval = 0x02000000; /* 536,870,912 seconds OR ~ 17 years */
TEST_ASSERT_EQUAL( SntpSuccess, Sntp_CalculatePollInterval( 1 /* Clock Tolerance (PPM) */,
UINT16_MAX /* Desired Accuracy (ms)*/,
&pollInterval ) );
TEST_ASSERT_EQUAL( expectedInterval, pollInterval );
}
/**
* @brief Tests the @ref Sntp_ConvertToUnixTime utility function returns
* expected error when invalid parameters or unsupported timestamps are passed.
*/
void test_ConvertToUnixTime_InvalidParams( void )
{
SntpTimestamp_t sntpTime;
/* Use same memory for UNIX seconds and microseconds as we are not
* testing those values. */
uint32_t unixTime;
/* Test with NULL SNTP time. */
TEST_ASSERT_EQUAL( SntpErrorBadParameter, Sntp_ConvertToUnixTime( NULL,
&unixTime,
&unixTime ) );
/* Test with NULL output parameters. */
TEST_ASSERT_EQUAL( SntpErrorBadParameter, Sntp_ConvertToUnixTime( &sntpTime,
NULL,
&unixTime ) );
TEST_ASSERT_EQUAL( SntpErrorBadParameter, Sntp_ConvertToUnixTime( &sntpTime,
&unixTime,
NULL ) );
/* Test with time before UNIX epoch or 1st Jan 1970 .*/
sntpTime.seconds = SNTP_TIME_AT_UNIX_EPOCH_SECS - 5;
TEST_ASSERT_EQUAL( SntpErrorTimeNotSupported, Sntp_ConvertToUnixTime( &sntpTime,
&unixTime,
&unixTime ) );
/* Test with timestamp that after largest UNIX time for signed 32-bit integer systems
* (i.e. after 18 Jan 2036 3:14:07) */
sntpTime.seconds = SNTP_TIME_AT_LARGEST_UNIX_TIME_SECS + 5;
TEST_ASSERT_EQUAL( SntpErrorTimeNotSupported, Sntp_ConvertToUnixTime( &sntpTime,
&unixTime,
&unixTime ) );
}
/**
* @brief Tests the @ref Sntp_ConvertToUnixTime utility function returns
* expected error when invalid parameters or unsupported timestamps are passed.
*/
void test_ConvertToUnixTime_Nominal( void )
{
SntpTimestamp_t sntpTime = TEST_TIMESTAMP;
uint32_t unixTimeSecs;
uint32_t unixTimeMs;
#define TEST_SNTP_TO_UNIX_CONVERSION( sntpTimeSecs, sntpTimeFracs, \
expectedUnixTimeSecs, expectedUnixTimeMs ) \
do { \
/* Set the SNTP timestamps. */ \
sntpTime.seconds = sntpTimeSecs; \
sntpTime.fractions = sntpTimeFracs; \
\
/* Call API under test. */ \
TEST_ASSERT_EQUAL( SntpSuccess, Sntp_ConvertToUnixTime( &sntpTime, \
&unixTimeSecs, \
&unixTimeMs ) ); \
/* Validate the generated UNIX time. */ \
TEST_ASSERT_EQUAL( expectedUnixTimeSecs, unixTimeSecs ); \
TEST_ASSERT_EQUAL( expectedUnixTimeMs, unixTimeMs ); \
} while( 0 )
/* Test with SNTP time at UNIX epoch. .*/
TEST_SNTP_TO_UNIX_CONVERSION( SNTP_TIME_AT_UNIX_EPOCH_SECS, /* Sntp Seconds. */
0 /* Sntp Fractions. */,
0 /* Unix Seconds */,
0 /* Unix Microseconds */ );
/* Test with SNTP time in the range between UNIX epoch and Smallest SNTP time in Era 1 .*/
TEST_SNTP_TO_UNIX_CONVERSION( SNTP_TIME_AT_UNIX_EPOCH_SECS + 1000 /* Sntp Seconds. */,
500 /* Sntp Fractions. */,
1000 /* Unix Seconds */,
500 / SNTP_FRACTION_VALUE_PER_MICROSECOND /* Unix Microseconds */ );
/* Test with SNTP time at largest SNTP time in Era 0 .*/
TEST_SNTP_TO_UNIX_CONVERSION( UINT32_MAX /* Sntp Seconds. */,
0 /* Sntp Fractions. */,
UINT32_MAX - SNTP_TIME_AT_UNIX_EPOCH_SECS, /* Unix Seconds */
0 /* Unix Microseconds */ );
/* Test with SNTP time at smallest SNTP time in Era 1 .*/
TEST_SNTP_TO_UNIX_CONVERSION( UINT32_MAX + 1 /* Sntp Seconds. */,
0 /* Sntp Fractions. */,
UNIX_TIME_SECS_AT_SNTP_ERA_1_SMALLEST_TIME, /* Unix Seconds */
0 /* Unix Microseconds */ );
/* Test SNTP time in the range [SNTP Era 1 Epoch, 32-bit signed UNIX max time] .*/
TEST_SNTP_TO_UNIX_CONVERSION( UINT32_MAX + 1000 /* Sntp Seconds. */,
4000 /* Sntp Fractions. */,
UNIX_TIME_SECS_AT_SNTP_ERA_1_SMALLEST_TIME + 999, /* Unix Seconds */
4000 / SNTP_FRACTION_VALUE_PER_MICROSECOND /* Unix Microseconds */ );
/* Test with SNTP time that represents the 32-bit signed maximum UNIX time
* (i.e. at 19 Jan 2038 3:14:07 ) .*/
TEST_SNTP_TO_UNIX_CONVERSION( SNTP_TIME_AT_LARGEST_UNIX_TIME_SECS /* Sntp Seconds. */,
0 /* Sntp Fractions. */,
INT32_MAX, /* Unix Seconds */
0 /* Unix Microseconds */ );
}

View File

@ -0,0 +1,38 @@
# Macro utility to clone the Unity submodule.
macro( clone_unity )
find_package( Git REQUIRED )
message( "Cloning submodule Unity." )
execute_process( COMMAND rm -rf ${UNITY_DIR}
COMMAND ${GIT_EXECUTABLE} submodule update --checkout --init --recursive ${UNITY_DIR}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE UNITY_CLONE_RESULT )
if( NOT ${UNITY_CLONE_RESULT} STREQUAL "0" )
message( FATAL_ERROR "Failed to clone Unity submodule." )
endif()
endmacro()
# Macro utility to add library targets for Unity and Unity to build configuration.
macro( add_unity_targets )
# Build Configuration for Unity and Unity libraries.
list( APPEND UNITY_INCLUDE_DIRS
"${UNITY_DIR}/src/"
"${UNITY_DIR}/extras/fixture/src"
"${UNITY_DIR}/extras/memory/src"
)
add_library( unity STATIC
"${UNITY_DIR}/src/unity.c"
"${UNITY_DIR}/extras/fixture/src/unity_fixture.c"
"${UNITY_DIR}/extras/memory/src/unity_memory.c"
)
set_target_properties( unity PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
POSITION_INDEPENDENT_CODE ON
)
target_include_directories( unity PUBLIC
${UNITY_INCLUDE_DIRS}
)
endmacro()

View File

@ -0,0 +1,70 @@
# Taken from amazon-freertos repository
cmake_minimum_required(VERSION 3.13)
set(BINARY_DIR ${CMAKE_BINARY_DIR})
# reset coverage counters
execute_process(
COMMAND lcov --directory ${CMAKE_BINARY_DIR}
--base-directory ${CMAKE_BINARY_DIR}
--zerocounters
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/coverage
)
# make the initial/baseline capture a zeroed out files
execute_process( COMMAND lcov --directory ${CMAKE_BINARY_DIR}
--base-directory ${CMAKE_BINARY_DIR}
--initial
--capture
--rc lcov_branch_coverage=1
--rc genhtml_branch_coverage=1
--output-file=${CMAKE_BINARY_DIR}/base_coverage.info
)
file(GLOB files "${CMAKE_BINARY_DIR}/bin/tests/*")
set(REPORT_FILE ${CMAKE_BINARY_DIR}/utest_report.txt)
file(WRITE ${REPORT_FILE} "")
# execute all files in bin directory, gathering the output to show it in CI
foreach(testname ${files})
get_filename_component(test
${testname}
NAME_WLE
)
message("Running ${testname}")
execute_process(COMMAND ${testname} OUTPUT_FILE ${CMAKE_BINARY_DIR}/${test}_out.txt)
file(READ ${CMAKE_BINARY_DIR}/${test}_out.txt CONTENTS)
file(APPEND ${REPORT_FILE} "${CONTENTS}")
endforeach()
# generate Junit style xml output
execute_process(COMMAND ruby
${UNITY_DIR}/auto/parse_output.rb
-xml ${REPORT_FILE}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
# capture data after running the tests
execute_process(
COMMAND lcov --capture
--rc lcov_branch_coverage=1
--rc genhtml_branch_coverage=1
--base-directory ${CMAKE_BINARY_DIR}
--directory ${CMAKE_BINARY_DIR}
--output-file ${CMAKE_BINARY_DIR}/second_coverage.info
)
# combile baseline results (zeros) with the one after running the tests
execute_process(
COMMAND lcov --base-directory ${CMAKE_BINARY_DIR}
--directory ${CMAKE_BINARY_DIR}
--add-tracefile ${CMAKE_BINARY_DIR}/base_coverage.info
--add-tracefile ${CMAKE_BINARY_DIR}/second_coverage.info
--output-file ${CMAKE_BINARY_DIR}/coverage.info
--no-external
--rc lcov_branch_coverage=1
)
execute_process(
COMMAND genhtml --rc lcov_branch_coverage=1
--branch-coverage
--output-directory ${CMAKE_BINARY_DIR}/coverage
${CMAKE_BINARY_DIR}/coverage.info
)

View File

@ -0,0 +1,168 @@
# Taken from amazon-freertos repository
#function to create the test executable
function(create_test test_name
test_src
link_list
dep_list
include_list)
set(mocks_dir "${CMAKE_CURRENT_BINARY_DIR}/mocks")
include (CTest)
get_filename_component(test_src_absolute ${test_src} ABSOLUTE)
add_custom_command(OUTPUT ${test_name}_runner.c
COMMAND ruby
${CMOCK_DIR}/vendor/unity/auto/generate_test_runner.rb
${MODULE_ROOT_DIR}/tools/cmock/project.yml
${test_src_absolute}
${test_name}_runner.c
DEPENDS ${test_src}
)
add_executable(${test_name} ${test_src} ${test_name}_runner.c)
set_target_properties(${test_name} PROPERTIES
COMPILE_FLAG "-O0 -ggdb"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/tests"
INSTALL_RPATH_USE_LINK_PATH TRUE
LINK_FLAGS " \
-Wl,-rpath,${CMAKE_BINARY_DIR}/lib \
-Wl,-rpath,${CMAKE_CURRENT_BINARY_DIR}/lib"
)
target_include_directories(${test_name} PUBLIC
${mocks_dir}
${include_list}
)
target_link_directories(${test_name} PUBLIC
${CMAKE_CURRENT_BINARY_DIR}
)
# link all libraries sent through parameters
foreach(link IN LISTS link_list)
target_link_libraries(${test_name} ${link})
endforeach()
# add dependency to all the dep_list parameter
foreach(dependency IN LISTS dep_list)
add_dependencies(${test_name} ${dependency})
target_link_libraries(${test_name} ${dependency})
endforeach()
target_link_libraries(${test_name} -lgcov unity)
target_link_directories(${test_name} PUBLIC
${CMAKE_CURRENT_BINARY_DIR}/lib
)
add_test(NAME ${test_name}
COMMAND ${CMAKE_BINARY_DIR}/bin/tests/${test_name}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
endfunction()
# Run the C preprocessor on target files.
# Takes a CMAKE list of arguments to pass to the C compiler
function(preprocess_mock_list mock_name file_list compiler_args)
set_property(GLOBAL PROPERTY ${mock_name}_processed TRUE)
foreach (target_file IN LISTS file_list)
# Has to be TARGET ALL so the file is pre-processed before CMOCK
# is executed on the file.
add_custom_command(OUTPUT ${target_file}.backup
COMMAND scp ${target_file} ${target_file}.backup
VERBATIM COMMAND ${CMAKE_C_COMPILER} -E ${compiler_args} ${target_file} > ${target_file}.out
)
add_custom_target(pre_${mock_name}
COMMAND mv ${target_file}.out ${target_file}
DEPENDS ${target_file}.backup
)
endforeach()
# Clean up temporary files that were created.
# First we test to see if the backup file still exists. If it does we revert
# the change made to the original file.
foreach (target_file IN LISTS file_list)
add_custom_command(TARGET ${mock_name}
POST_BUILD
COMMAND test ! -e ${target_file}.backup || mv ${target_file}.backup ${target_file}
)
endforeach()
endfunction()
# Generates a mock library based on a module's header file
# places the generated source file in the build directory
# @param mock_name: name of the target name
# @param mock_list list of header files to mock
# @param cmock_config configuration file of the cmock framework
# @param mock_include_list include list for the target
# @param mock_define_list special definitions to control compilation
function(create_mock_list mock_name
mock_list
cmock_config
mock_include_list
mock_define_list)
set(mocks_dir "${CMAKE_CURRENT_BINARY_DIR}/mocks")
add_library(${mock_name} SHARED)
foreach (mock_file IN LISTS mock_list)
get_filename_component(mock_file_abs
${mock_file}
ABSOLUTE
)
get_filename_component(mock_file_name
${mock_file}
NAME_WLE
)
get_filename_component(mock_file_dir
${mock_file}
DIRECTORY
)
add_custom_command (
OUTPUT ${mocks_dir}/mock_${mock_file_name}.c
COMMAND ruby
${CMOCK_DIR}/lib/cmock.rb
-o${cmock_config} ${mock_file_abs}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
target_sources(${mock_name} PUBLIC
${mocks_dir}/mock_${mock_file_name}.c
)
target_include_directories(${mock_name} PUBLIC
${mock_file_dir}
)
endforeach()
target_include_directories(${mock_name} PUBLIC
${mocks_dir}
${mock_include_list}
)
set_target_properties(${mock_name} PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib
POSITION_INDEPENDENT_CODE ON
)
target_compile_definitions(${mock_name} PUBLIC
${mock_define_list}
)
target_link_libraries(${mock_name} cmock unity)
endfunction()
function(create_real_library target
src_file
real_include_list
mock_name)
add_library(${target} STATIC
${src_file}
)
target_include_directories(${target} PUBLIC
${real_include_list}
)
set_target_properties(${target} PROPERTIES
COMPILE_FLAGS "-Wextra -Wpedantic \
-fprofile-arcs -ftest-coverage -fprofile-generate \
-Wno-unused-but-set-variable"
LINK_FLAGS "-fprofile-arcs -ftest-coverage \
-fprofile-generate "
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib
)
if(NOT(mock_name STREQUAL ""))
add_dependencies(${target} ${mock_name})
target_link_libraries(${target}
-l${mock_name}
-lgcov
)
endif()
endfunction()

View File

@ -0,0 +1,26 @@
# Taken from amazon-freertos repository
:cmock:
:mock_prefix: mock_
:when_no_prototypes: :warn
:enforce_strict_ordering: TRUE
:plugins:
- :ignore
- :ignore_arg
- :expect_any_args
- :array
- :callback
- :return_thru_ptr
:callback_include_count: true # include a count arg when calling the callback
:callback_after_arg_check: false # check arguments before calling the callback
:treat_as:
uint8: HEX8
uint16: HEX16
uint32: UINT32
int8: INT8
bool: UINT8
:includes: # This will add these includes to each mock.
- <stdbool.h>
- <stdint.h>
:treat_externs: :exclude # Now the extern-ed functions will be mocked.
:weak: __attribute__((weak))
:treat_externs: :include

View File

@ -0,0 +1,79 @@
# Static code analysis for coreSNTP Library
This directory is made for the purpose of statically testing the MISRA C:2012 compliance of coreSNTP Library using
[Synopsys Coverity](https://www.synopsys.com/software-integrity/security-testing/static-analysis-sast.html) static analysis tool.
To that end, this directory provides a [configuration file](https://github.com/FreeRTOS/coreSNTP/blob/main/tools/coverity/misra.config) to use when
building a binary for the tool to analyze.
> **Note**
For generating the report as outlined below, we have used Coverity version 2018.09.
For details regarding the suppressed violations in the report (which can be generated using the instructions described below), please
see the [MISRA.md](https://github.com/FreeRTOS/coreSNTP/blob/main/MISRA.md) file.
## Getting Started
### Prerequisites
You can run this on a platform supported by Coverity. The list and other details can be found [here](https://sig-docs.synopsys.com/polaris/topics/c_coverity-compatible-platforms.html).
To compile and run the Coverity target successfully, you must have the following:
1. CMake version > 3.13.0 (You can check whether you have this by typing `cmake --version`)
2. GCC compiler
- You can see the downloading and installation instructions [here](https://gcc.gnu.org/install/).
3. Download the repo and include the submodules using the following commands.
- `git clone --recurse-submodules git@github.com:FreeRTOS/coreSNTP.git ./coreSNTP`
- `cd ./coreSNTP`
- `git submodule update --checkout --init --recursive`
### To build and run coverity:
Go to the root directory of the library and run the following commands in terminal:
1. Update the compiler configuration in Coverity
~~~
cov-configure --force --compiler cc --comptype gcc
~~~
2. Create the build files using CMake in a `build` directory
~~~
cmake -B build -S test
~~~
3. Go to the build directory and copy the coverity configuration file
~~~
cd build/
~~~
4. Build the static analysis target
~~~
cov-build --emit-complementary-info --dir cov-out make coverity_analysis
~~~
5. Go to the Coverity output directory (`cov-out`) and begin Coverity static analysis
~~~
cd cov-out/
cov-analyze --dir . --coding-standard-config ../../tools/coverity/misra.config --tu-pattern "file('.*/source/.*')"
~~~
6. Format the errors in HTML format so that it is more readable while removing the test and build directory from the report
~~~
cov-format-errors --dir . --file "source" --exclude-files '(/build/|/test/)' --html-output html-out;
~~~
7. Format the errors in JSON format to perform a jq query to get a simplified list of any exceptions.
NOTE: A blank output means there are no defects that aren't being suppressed by the config or inline comments.
~~~
cov-format-errors --dir . --file "source" --exclude-files '(/build/|/test/)' --json-output-v2 defects.json;
echo -e "\n-------------------------Non-Suppresed Deviations, if any, Listed Below-------------------------\n";
jq '.issues[] | .events[] | .eventTag ' defects.json | sort | uniq -c | sort -nr;
echo -e "\n-------------------------Non-Suppresed Deviations, if any, Listed Above-------------------------\n";
~~~
For your convenience the commands above are below to be copy/pasted into a UNIX command friendly terminal.
~~~
cov-configure --force --compiler cc --comptype gcc;
cmake -B build -S test;
cd build/;
cov-build --emit-complementary-info --dir cov-out make coverity_analysis;
cd cov-out/
cov-analyze --dir . --coding-standard-config ../../tools/coverity/misra.config --tu-pattern "file('.*/source/.*')";
cov-format-errors --dir . --file "source" --exclude-files '(/build/|/test/)' --html-output html-out;
cov-format-errors --dir . --file "source" --exclude-files '(/build/|/test/)' --json-output-v2 defects.json;
echo -e "\n-------------------------Non-Suppresed Deviations, if any, Listed Below-------------------------\n";
jq '.issues[] | .events[] | .eventTag ' defects.json | sort | uniq -c | sort -nr;
echo -e "\n-------------------------Non-Suppresed Deviations, if any, Listed Above-------------------------\n";
cd ../../;
~~~
You should now have the HTML formatted violations list in a directory named `build/cov-out/html-output`.
With the current configuration and the provided project, you should not see any deviations.

View File

@ -0,0 +1,30 @@
// MISRA C-2012 Rules
{
version : "2.0",
standard : "c2012",
title: "Coverity MISRA Configuration",
deviations : [
// Disable the following rules.
{
deviation: "Directive 4.9",
reason: "Allow inclusion of function like macros. Asserts and logging are done using function like macros."
},
{
deviation: "Rule 2.4",
reason: "Allow unused tags. Some compilers warn if types are not tagged."
},
{
deviation: "Rule 2.5",
reason: "Allow unused macros. coreSNTP Library headers define macros intended for the application's use, but are not used by the agent."
},
{
deviation: "Rule 3.1",
reason: "Allow nested comments. Documentation blocks contain comments for example code."
},
{
deviation: "Rule 8.7",
reason: "API functions are not used by library. They must be externally visible in order to be used by the application."
},
]
}