├── .editorconfig ├── .github └── workflows │ ├── ci.yml │ └── docs.yml ├── .gitignore ├── .jenkins ├── .jenkins.d ├── 00-deps.sh ├── 01-ndn-cxx.sh ├── 09-cleanup.sh ├── 10-build.sh ├── 20-tests.sh ├── 30-coverage.sh ├── 40-headers-check.sh └── README.md ├── .mailmap ├── .waf-tools ├── boost.py ├── coverage.py ├── default-compiler-flags.py ├── doxygen.py ├── sanitizers.py └── sphinx.py ├── AUTHORS.md ├── COPYING.lesser ├── COPYING.md ├── README.md ├── docs ├── _static │ ├── access-manager.png │ ├── decryptor.png │ ├── encryptor.png │ └── nac-overview.png ├── conf.py ├── doxygen.conf.in ├── figures │ ├── access-manager.pptx │ ├── decryptor.pptx │ ├── encryptor.pptx │ └── nac-overview.pptx ├── index.rst ├── named_data_theme │ ├── layout.html │ ├── named_data_footer-with-analytics.html.in │ ├── named_data_footer.html │ ├── named_data_header.html │ ├── static │ │ ├── bar-top.png │ │ ├── base.css │ │ ├── base.css_t │ │ ├── bc_s.png │ │ ├── default.css_t │ │ ├── doxygen.css │ │ ├── foundation.css │ │ ├── named_data_doxygen.css │ │ ├── named_data_style.css_t │ │ ├── nav_f.png │ │ └── tab_b.png │ └── theme.conf ├── requirements.txt └── spec.rst ├── examples ├── consumer.cpp ├── producer.cpp └── wscript ├── libndn-nac.pc.in ├── src ├── access-manager.cpp ├── access-manager.hpp ├── common.cpp ├── common.hpp ├── decryptor.cpp ├── decryptor.hpp ├── encrypted-content.cpp ├── encrypted-content.hpp ├── encryptor.cpp ├── encryptor.hpp └── version.hpp.in ├── tests ├── boost-test.hpp ├── clock-fixture.cpp ├── clock-fixture.hpp ├── io-key-chain-fixture.hpp ├── key-chain-fixture.hpp ├── main.cpp ├── unit │ ├── access-manager.t.cpp │ ├── common.t.cpp │ ├── decryptor.t.cpp │ ├── encrypted-content.t.cpp │ ├── encryptor.t.cpp │ └── static-data.hpp └── wscript ├── tools ├── ndn-nac │ ├── add-member.cpp │ ├── dump-kek.cpp │ ├── main.cpp │ └── ndn-nac.hpp └── wscript ├── waf └── wscript /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | 8 | [*.{cpp,hpp}] 9 | indent_style = space 10 | indent_size = 2 11 | trim_trailing_whitespace = true 12 | 13 | [{*.{py,sh},.jenkins,wscript}] 14 | indent_style = space 15 | indent_size = 4 16 | trim_trailing_whitespace = true 17 | 18 | [*.md] 19 | indent_style = space 20 | trim_trailing_whitespace = false 21 | 22 | [*.rst] 23 | indent_style = space 24 | trim_trailing_whitespace = true 25 | 26 | [*.{yaml,yml}] 27 | indent_style = space 28 | indent_size = 2 29 | trim_trailing_whitespace = true 30 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - '**' 6 | workflow_dispatch: 7 | 8 | permissions: {} 9 | 10 | jobs: 11 | Ubuntu: 12 | uses: named-data/actions/.github/workflows/jenkins-script-ubuntu.yml@v1 13 | macOS: 14 | uses: named-data/actions/.github/workflows/jenkins-script-macos.yml@v1 15 | PPA: 16 | uses: named-data/actions/.github/workflows/ppa.yml@v1 17 | with: 18 | extra-deps: libboost-program-options-dev 19 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | on: 3 | push: 4 | workflow_dispatch: 5 | inputs: 6 | skip-deploy: 7 | description: 'Skip deployment?' 8 | required: true 9 | type: boolean 10 | 11 | permissions: {} 12 | 13 | jobs: 14 | html: 15 | uses: named-data/actions/.github/workflows/docs-html.yml@v1 16 | with: 17 | # Deploy the 'new' branch as 'latest' 18 | deploy: ${{ !inputs.skip-deploy && github.ref == 'refs/heads/new' }} 19 | project: NAC 20 | version: latest 21 | secrets: inherit 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Backup files 2 | *~ 3 | *.bak 4 | *.orig 5 | *.rej 6 | 7 | # Waf build system 8 | /build/ 9 | .waf-*-*/ 10 | .waf3-*-*/ 11 | .lock-waf* 12 | 13 | # Compiled python code 14 | __pycache__/ 15 | *.py[cod] 16 | 17 | # Emacs 18 | \#*\# 19 | /.emacs.desktop 20 | /.emacs.desktop.lock 21 | *.elc 22 | .\#* 23 | 24 | # Visual Studio Code 25 | .vscode/ 26 | 27 | # macOS 28 | .DS_Store 29 | .AppleDouble 30 | .LSOverride 31 | ._* 32 | 33 | # Other 34 | /VERSION.info 35 | -------------------------------------------------------------------------------- /.jenkins: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | case $(uname) in 5 | Linux) 6 | if [[ -e /etc/os-release ]]; then 7 | source /etc/os-release 8 | else 9 | source /usr/lib/os-release 10 | fi 11 | export ID VERSION_ID 12 | export ID_LIKE="${ID} ${ID_LIKE} linux" 13 | if [[ -z $GITHUB_ACTIONS ]]; then 14 | export PATH="${HOME}/.local/bin${PATH:+:}${PATH}" 15 | fi 16 | ;; 17 | Darwin) 18 | # Emulate a subset of os-release(5) 19 | export ID=macos 20 | export VERSION_ID=$(sw_vers -productVersion) 21 | if [[ -z $GITHUB_ACTIONS ]]; then 22 | export PATH="/usr/local/bin${PATH:+:}${PATH}" 23 | fi 24 | if [[ -x /opt/homebrew/bin/brew ]]; then 25 | eval "$(/opt/homebrew/bin/brew shellenv)" 26 | elif [[ -x /usr/local/bin/brew ]]; then 27 | eval "$(/usr/local/bin/brew shellenv)" 28 | fi 29 | ;; 30 | esac 31 | 32 | export CACHE_DIR=${CACHE_DIR:-/tmp} 33 | 34 | if [[ $JOB_NAME == *code-coverage ]]; then 35 | export DISABLE_ASAN=1 36 | export DISABLE_HEADERS_CHECK=1 37 | fi 38 | 39 | # https://reproducible-builds.org/docs/source-date-epoch/ 40 | export SOURCE_DATE_EPOCH=$(git log -1 --format=%ct) 41 | 42 | for file in .jenkins.d/*; do 43 | [[ -f $file && -x $file ]] || continue 44 | 45 | if [[ -n $GITHUB_ACTIONS ]]; then 46 | label=$(basename "$file" | sed -E 's/[[:digit:]]+-(.*)\..*/\1/') 47 | echo "::group::${label}" 48 | fi 49 | 50 | echo "\$ $file" 51 | "$file" 52 | 53 | if [[ -n $GITHUB_ACTIONS ]]; then 54 | echo "::endgroup::" 55 | fi 56 | done 57 | -------------------------------------------------------------------------------- /.jenkins.d/00-deps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | APT_PKGS=( 5 | dpkg-dev 6 | g++ 7 | libboost-chrono-dev 8 | libboost-date-time-dev 9 | libboost-dev 10 | libboost-log-dev 11 | libboost-program-options-dev 12 | libboost-stacktrace-dev 13 | libboost-test-dev 14 | libboost-thread-dev 15 | libsqlite3-dev 16 | libssl-dev 17 | pkgconf 18 | python3 19 | ) 20 | DNF_PKGS=( 21 | boost-devel 22 | gcc-c++ 23 | libasan 24 | lld 25 | openssl-devel 26 | pkgconf 27 | python3 28 | sqlite-devel 29 | ) 30 | FORMULAE=(boost openssl pkgconf) 31 | case $JOB_NAME in 32 | *code-coverage) 33 | APT_PKGS+=(lcov libjson-xs-perl) 34 | ;; 35 | *Docs) 36 | APT_PKGS+=(doxygen graphviz) 37 | FORMULAE+=(doxygen graphviz) 38 | ;; 39 | esac 40 | 41 | install_uv() { 42 | if [[ -z $GITHUB_ACTIONS && $ID_LIKE == *debian* ]]; then 43 | sudo apt-get install -qy --no-install-recommends pipx 44 | pipx upgrade uv || pipx install uv 45 | fi 46 | } 47 | 48 | set -x 49 | 50 | if [[ $ID == macos ]]; then 51 | export HOMEBREW_COLOR=1 52 | export HOMEBREW_NO_ENV_HINTS=1 53 | if [[ -n $GITHUB_ACTIONS ]]; then 54 | export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 55 | fi 56 | brew update 57 | brew install --formula "${FORMULAE[@]}" 58 | elif [[ $ID_LIKE == *debian* ]]; then 59 | sudo apt-get update -qq 60 | sudo apt-get install -qy --no-install-recommends "${APT_PKGS[@]}" 61 | elif [[ $ID_LIKE == *fedora* ]]; then 62 | sudo dnf install -y "${DNF_PKGS[@]}" 63 | fi 64 | 65 | case $JOB_NAME in 66 | *code-coverage) 67 | install_uv 68 | ;; 69 | *Docs) 70 | install_uv 71 | export FORCE_COLOR=1 72 | export UV_NO_MANAGED_PYTHON=1 73 | uv tool install sphinx --upgrade --with-requirements docs/requirements.txt 74 | ;; 75 | esac 76 | -------------------------------------------------------------------------------- /.jenkins.d/01-ndn-cxx.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -exo pipefail 3 | 4 | pushd "$CACHE_DIR" >/dev/null 5 | 6 | INSTALLED_VERSION= 7 | if [[ $ID == macos ]]; then 8 | BOOST=$(brew list --formula --versions boost) 9 | OLD_BOOST=$(cat boost.txt || :) 10 | if [[ $OLD_BOOST != $BOOST ]]; then 11 | echo "$BOOST" > boost.txt 12 | INSTALLED_VERSION=NONE 13 | fi 14 | fi 15 | 16 | if [[ -z $INSTALLED_VERSION ]]; then 17 | INSTALLED_VERSION=$(git -C ndn-cxx rev-parse HEAD 2>/dev/null || echo NONE) 18 | fi 19 | 20 | sudo rm -rf ndn-cxx-latest 21 | git clone --depth 1 https://github.com/named-data/ndn-cxx.git ndn-cxx-latest 22 | LATEST_VERSION=$(git -C ndn-cxx-latest rev-parse HEAD 2>/dev/null || echo UNKNOWN) 23 | 24 | if [[ $INSTALLED_VERSION != $LATEST_VERSION ]]; then 25 | sudo rm -rf ndn-cxx 26 | mv ndn-cxx-latest ndn-cxx 27 | else 28 | sudo rm -rf ndn-cxx-latest 29 | fi 30 | 31 | sudo rm -f /usr/local/bin/ndnsec* 32 | sudo rm -fr /usr/local/include/ndn-cxx 33 | sudo rm -f /usr/local/lib{,64}/libndn-cxx* 34 | sudo rm -f /usr/local/lib{,64}/pkgconfig/libndn-cxx.pc 35 | 36 | pushd ndn-cxx >/dev/null 37 | 38 | ./waf --color=yes configure 39 | ./waf --color=yes build 40 | sudo ./waf --color=yes install 41 | 42 | popd >/dev/null 43 | popd >/dev/null 44 | 45 | if [[ $ID_LIKE == *linux* ]]; then 46 | if [[ $(uname -m) == x86_64 && -d /usr/lib64 ]]; then 47 | sudo tee /etc/ld.so.conf.d/ndn.conf >/dev/null <<< /usr/local/lib64 48 | fi 49 | sudo ldconfig 50 | fi 51 | -------------------------------------------------------------------------------- /.jenkins.d/09-cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -exo pipefail 3 | 4 | PROJ=ndn-nac 5 | 6 | sudo rm -fr /usr/local/include/"$PROJ" 7 | sudo rm -f /usr/local/lib{,64}/lib"$PROJ"* 8 | sudo rm -f /usr/local/lib{,64}/pkgconfig/{,lib}"$PROJ".pc 9 | -------------------------------------------------------------------------------- /.jenkins.d/10-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | if [[ -z $DISABLE_ASAN ]]; then 5 | ASAN="--with-sanitizer=address" 6 | fi 7 | if [[ $JOB_NAME == *code-coverage ]]; then 8 | COVERAGE="--with-coverage" 9 | fi 10 | 11 | set -x 12 | 13 | if [[ $JOB_NAME != *code-coverage && $JOB_NAME != *limited-build ]]; then 14 | # Build in release mode with tests 15 | ./waf --color=yes configure --with-tests 16 | ./waf --color=yes build 17 | 18 | # Cleanup 19 | ./waf --color=yes distclean 20 | 21 | # Build in release mode with examples 22 | ./waf --color=yes configure --with-examples 23 | ./waf --color=yes build 24 | 25 | # Cleanup 26 | ./waf --color=yes distclean 27 | fi 28 | 29 | # Build in debug mode with tests 30 | ./waf --color=yes configure --debug --with-tests $ASAN $COVERAGE 31 | ./waf --color=yes build 32 | 33 | # (tests will be run against the debug version) 34 | 35 | # Install 36 | sudo ./waf --color=yes install 37 | 38 | if [[ $ID_LIKE == *linux* ]]; then 39 | if [[ $(uname -m) == x86_64 && -d /usr/lib64 ]]; then 40 | sudo tee /etc/ld.so.conf.d/ndn.conf >/dev/null <<< /usr/local/lib64 41 | fi 42 | sudo ldconfig 43 | fi 44 | -------------------------------------------------------------------------------- /.jenkins.d/20-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | # https://github.com/google/sanitizers/wiki/SanitizerCommonFlags 5 | # https://github.com/google/sanitizers/wiki/AddressSanitizerFlags 6 | ASAN_OPTIONS="color=always" 7 | ASAN_OPTIONS+=":strip_path_prefix=${PWD}/" 8 | ASAN_OPTIONS+=":check_initialization_order=1" 9 | ASAN_OPTIONS+=":detect_stack_use_after_return=1" 10 | ASAN_OPTIONS+=":strict_init_order=1" 11 | ASAN_OPTIONS+=":strict_string_checks=1" 12 | ASAN_OPTIONS+=":detect_invalid_pointer_pairs=2" 13 | ASAN_OPTIONS+=":detect_container_overflow=0" 14 | export ASAN_OPTIONS 15 | 16 | # https://www.boost.org/doc/libs/release/libs/test/doc/html/boost_test/runtime_config/summary.html 17 | export BOOST_TEST_BUILD_INFO=1 18 | export BOOST_TEST_COLOR_OUTPUT=1 19 | export BOOST_TEST_DETECT_MEMORY_LEAK=0 20 | export BOOST_TEST_LOGGER=HRF,test_suite,stdout:XML,all,build/xunit-log.xml 21 | 22 | set -x 23 | 24 | # Prepare environment 25 | rm -rf ~/.ndn 26 | 27 | # Run unit tests 28 | ./build/unit-tests 29 | -------------------------------------------------------------------------------- /.jenkins.d/30-coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | [[ $JOB_NAME == *code-coverage ]] || exit 0 5 | 6 | export FORCE_COLOR=1 7 | export UV_NO_MANAGED_PYTHON=1 8 | 9 | set -x 10 | 11 | # Generate a detailed HTML report and an XML report in Cobertura format using gcovr 12 | # Note: trailing slashes are important in the paths below. Do not remove them! 13 | uvx --from 'git+https://github.com/gcovr/gcovr@99b82e7' gcovr \ 14 | --decisions \ 15 | --filter src/ \ 16 | --exclude-throw-branches \ 17 | --exclude-unreachable-branches \ 18 | --cobertura build/coverage.xml \ 19 | --html-details build/gcovr/ \ 20 | --txt-summary \ 21 | build 22 | 23 | # Generate a detailed HTML report using lcov 24 | lcov \ 25 | --quiet \ 26 | --capture \ 27 | --directory . \ 28 | --include "$PWD/src/*" \ 29 | --branch-coverage \ 30 | --rc no_exception_branch=1 \ 31 | --ignore-errors inconsistent,mismatch,mismatch \ 32 | --output-file build/coverage.info 33 | 34 | genhtml \ 35 | --quiet \ 36 | --branch-coverage \ 37 | --demangle-cpp \ 38 | --flat \ 39 | --legend \ 40 | --missed \ 41 | --show-proportion \ 42 | --title "ndn-nac $(cat VERSION.info)" \ 43 | --ignore-errors inconsistent,inconsistent \ 44 | --output-directory build/lcov \ 45 | build/coverage.info 46 | -------------------------------------------------------------------------------- /.jenkins.d/40-headers-check.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | # It's intentional not to use `set -x`, because this script explicitly prints useful information 4 | # and should not run in trace mode. 5 | 6 | PROJ=ndn-nac 7 | PCFILE=libndn-nac 8 | 9 | if [[ -n $DISABLE_HEADERS_CHECK ]]; then 10 | echo 'Skipping headers check.' 11 | exit 0 12 | fi 13 | 14 | if [[ $ID_LIKE == *linux* && -d /usr/local/lib64/pkgconfig ]]; then 15 | export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig 16 | fi 17 | 18 | CXX=${CXX:-g++} 19 | STD=-std=c++17 20 | CXXFLAGS="-O2 -Wall -Wno-unknown-warning-option -Wno-enum-constexpr-conversion -Wno-unused-const-variable $(pkgconf --cflags libndn-cxx $PCFILE)" 21 | INCLUDEDIR="$(pkgconf --variable=includedir $PCFILE)"/$PROJ 22 | 23 | echo "Using: $CXX $STD $CXXFLAGS" 24 | 25 | NCHECKED=0 26 | NERRORS=0 27 | while IFS= read -r -d '' H; do 28 | echo "Checking header ${H#${INCLUDEDIR}/}" 29 | "$CXX" -xc++ $STD $CXXFLAGS -c -o /dev/null "$H" || : $((NERRORS++)) 30 | : $((NCHECKED++)) 31 | done < <(find "$INCLUDEDIR" -name '*.hpp' -type f -print0 2>/dev/null) 32 | 33 | if [[ $NCHECKED -eq 0 ]]; then 34 | echo "No headers found. Is $PROJ installed?" 35 | exit 1 36 | else 37 | echo "$NCHECKED headers checked." 38 | fi 39 | 40 | if [[ $NERRORS -gt 0 ]]; then 41 | echo "$NERRORS headers could not be compiled." 42 | exit 1 43 | fi 44 | -------------------------------------------------------------------------------- /.jenkins.d/README.md: -------------------------------------------------------------------------------- 1 | # Continuous Integration Scripts 2 | 3 | ## Environment Variables 4 | 5 | - `ID`: lower-case string that identifies the operating system, for example: `ID=ubuntu`, 6 | `ID=centos`. See [os-release(5)] for more information. On macOS, where `os-release` is 7 | not available, we emulate it by setting `ID=macos`. 8 | 9 | - `ID_LIKE`: space-separated list of operating system identifiers that are closely related 10 | to the running OS. See [os-release(5)] for more information. The listed values are used 11 | by the CI scripts to select the proper behavior for different platforms and OS flavors. 12 | 13 | Examples: 14 | 15 | - On CentOS, `ID_LIKE="centos rhel fedora linux"` 16 | - On Ubuntu, `ID_LIKE="ubuntu debian linux"` 17 | 18 | - `VERSION_ID`: identifies the operating system version, excluding any release code names. 19 | See [os-release(5)] for more information. Examples: `VERSION_ID=42`, `VERSION_ID=22.04`. 20 | 21 | - `JOB_NAME`: defines the type of the current CI job. Depending on the job type, the CI 22 | scripts can perform different tasks. 23 | 24 | Supported values: 25 | 26 | - empty: default build task 27 | - `code-coverage`: debug build with tests and code coverage analysis 28 | - `limited-build`: only a single debug build with tests 29 | 30 | - `CACHE_DIR`: directory containing cached files from previous builds, e.g., a compiled 31 | version of ndn-cxx. If not set, `/tmp` is used. 32 | 33 | - `DISABLE_ASAN`: disable building with AddressSanitizer. This is automatically set for 34 | the `code-coverage` job type. 35 | 36 | [os-release(5)]: https://www.freedesktop.org/software/systemd/man/os-release.html 37 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.waf-tools/coverage.py: -------------------------------------------------------------------------------- 1 | from waflib import TaskGen 2 | 3 | def options(opt): 4 | opt.add_option('--with-coverage', action='store_true', default=False, 5 | help='Add compiler flags to enable code coverage information') 6 | 7 | def configure(conf): 8 | if conf.options.with_coverage: 9 | if not conf.options.debug: 10 | conf.fatal('Code coverage flags require debug mode compilation (add --debug)') 11 | conf.check_cxx(cxxflags=['-fprofile-arcs', '-ftest-coverage', '-fPIC'], 12 | linkflags=['-fprofile-arcs'], uselib_store='GCOV', mandatory=True) 13 | 14 | @TaskGen.feature('cxx','cc') 15 | @TaskGen.after('process_source') 16 | def add_coverage(self): 17 | if getattr(self, 'use', ''): 18 | self.use += ' GCOV' 19 | else: 20 | self.use = 'GCOV' 21 | -------------------------------------------------------------------------------- /.waf-tools/default-compiler-flags.py: -------------------------------------------------------------------------------- 1 | import platform 2 | from waflib import Configure, Logs, Utils 3 | 4 | 5 | def options(opt): 6 | opt.add_option('--debug', '--with-debug', action='store_true', default=False, 7 | help='Compile in debugging mode with minimal optimizations (-Og)') 8 | 9 | 10 | def configure(conf): 11 | conf.start_msg('Checking C++ compiler version') 12 | 13 | cxx = conf.env.CXX_NAME # generic name of the compiler 14 | ccver = get_compiler_ver(conf) 15 | ccverstr = '.'.join(conf.env.CC_VERSION) 16 | errmsg = '' 17 | warnmsg = '' 18 | if cxx == 'gcc': 19 | if ccver < (9, 1, 0): 20 | errmsg = ('The version of gcc you are using is too old.\n' 21 | 'The minimum supported gcc version is 10.2.') 22 | elif ccver < (10, 2, 0): 23 | warnmsg = ('Using a version of gcc older than 10.2 is not ' 24 | 'officially supported and may result in build failures.') 25 | conf.flags = GccFlags() 26 | elif cxx == 'clang': 27 | if Utils.unversioned_sys_platform() == 'darwin': 28 | if ccver < (11, 0, 0): 29 | errmsg = ('The version of Xcode you are using is too old.\n' 30 | 'The minimum supported Xcode version is 13.0.') 31 | elif ccver < (13, 0, 0): 32 | warnmsg = ('Using a version of Xcode older than 13.0 is not ' 33 | 'officially supported and may result in build failures.') 34 | elif ccver < (7, 0, 0): 35 | errmsg = ('The version of clang you are using is too old.\n' 36 | 'The minimum supported clang version is 10.0.') 37 | elif ccver < (10, 0, 0): 38 | warnmsg = ('Using a version of clang older than 10.0 is not ' 39 | 'officially supported and may result in build failures.') 40 | conf.flags = ClangFlags() 41 | else: 42 | warnmsg = f'{cxx} compiler is unsupported' 43 | conf.flags = CompilerFlags() 44 | 45 | if errmsg: 46 | conf.end_msg(ccverstr, color='RED') 47 | conf.fatal(errmsg) 48 | elif warnmsg: 49 | conf.end_msg(ccverstr, color='YELLOW') 50 | Logs.warn('WARNING: ' + warnmsg) 51 | else: 52 | conf.end_msg(ccverstr) 53 | 54 | conf.areCustomCxxflagsPresent = len(conf.env.CXXFLAGS) > 0 55 | 56 | # General flags are always applied (e.g., selecting C++ language standard) 57 | generalFlags = conf.flags.getGeneralFlags(conf) 58 | conf.add_supported_cxxflags(generalFlags['CXXFLAGS']) 59 | conf.add_supported_linkflags(generalFlags['LINKFLAGS']) 60 | conf.env.DEFINES += generalFlags['DEFINES'] 61 | 62 | 63 | def get_compiler_ver(conf): 64 | return tuple(int(i) for i in conf.env.CC_VERSION) 65 | 66 | 67 | @Configure.conf 68 | def check_compiler_flags(conf): 69 | # Debug or optimized CXXFLAGS and LINKFLAGS are applied only if the 70 | # corresponding environment variables are not set. 71 | # DEFINES are always applied. 72 | if conf.options.debug: 73 | extraFlags = conf.flags.getDebugFlags(conf) 74 | if conf.areCustomCxxflagsPresent: 75 | missingFlags = [x for x in extraFlags['CXXFLAGS'] if x not in conf.env.CXXFLAGS] 76 | if missingFlags: 77 | Logs.warn('Selected debug mode, but CXXFLAGS is set to a custom value "%s"' 78 | % ' '.join(conf.env.CXXFLAGS)) 79 | Logs.warn('Default flags "%s" will not be used' % ' '.join(missingFlags)) 80 | else: 81 | extraFlags = conf.flags.getOptimizedFlags(conf) 82 | 83 | if not conf.areCustomCxxflagsPresent: 84 | conf.add_supported_cxxflags(extraFlags['CXXFLAGS']) 85 | conf.add_supported_linkflags(extraFlags['LINKFLAGS']) 86 | 87 | conf.env.DEFINES += extraFlags['DEFINES'] 88 | 89 | 90 | @Configure.conf 91 | def add_supported_cxxflags(self, cxxflags): 92 | """ 93 | Check which cxxflags are supported by the active compiler and add them to env.CXXFLAGS variable. 94 | """ 95 | if len(cxxflags) == 0: 96 | return 97 | 98 | self.start_msg('Checking supported CXXFLAGS') 99 | 100 | supportedFlags = [] 101 | for flags in cxxflags: 102 | flags = Utils.to_list(flags) 103 | if self.check_cxx(cxxflags=['-Werror'] + flags, mandatory=False): 104 | supportedFlags += flags 105 | 106 | self.end_msg(' '.join(supportedFlags)) 107 | self.env.prepend_value('CXXFLAGS', supportedFlags) 108 | 109 | 110 | @Configure.conf 111 | def add_supported_linkflags(self, linkflags): 112 | """ 113 | Check which linkflags are supported by the active compiler and add them to env.LINKFLAGS variable. 114 | """ 115 | if len(linkflags) == 0: 116 | return 117 | 118 | self.start_msg('Checking supported LINKFLAGS') 119 | 120 | supportedFlags = [] 121 | for flags in linkflags: 122 | flags = Utils.to_list(flags) 123 | if self.check_cxx(linkflags=['-Werror'] + flags, mandatory=False): 124 | supportedFlags += flags 125 | 126 | self.end_msg(' '.join(supportedFlags)) 127 | self.env.prepend_value('LINKFLAGS', supportedFlags) 128 | 129 | 130 | class CompilerFlags: 131 | def getGeneralFlags(self, conf): 132 | """Get dict of CXXFLAGS, LINKFLAGS, and DEFINES that are always needed""" 133 | return {'CXXFLAGS': [], 'LINKFLAGS': [], 'DEFINES': []} 134 | 135 | def getDebugFlags(self, conf): 136 | """Get dict of CXXFLAGS, LINKFLAGS, and DEFINES that are needed only in debug mode""" 137 | return { 138 | 'CXXFLAGS': [], 139 | 'LINKFLAGS': [], 140 | 'DEFINES': ['BOOST_ASIO_NO_DEPRECATED'], 141 | } 142 | 143 | def getOptimizedFlags(self, conf): 144 | """Get dict of CXXFLAGS, LINKFLAGS, and DEFINES that are needed only in optimized mode""" 145 | return {'CXXFLAGS': [], 'LINKFLAGS': [], 'DEFINES': ['NDEBUG']} 146 | 147 | 148 | class GccClangCommonFlags(CompilerFlags): 149 | """ 150 | This class defines common flags that work for both gcc and clang compilers. 151 | """ 152 | 153 | def getGeneralFlags(self, conf): 154 | flags = super().getGeneralFlags(conf) 155 | flags['CXXFLAGS'] += ['-std=c++17'] 156 | if Utils.unversioned_sys_platform() != 'darwin': 157 | flags['LINKFLAGS'] += ['-fuse-ld=lld'] 158 | return flags 159 | 160 | __cxxFlags = [ 161 | '-fdiagnostics-color', 162 | '-Wall', 163 | '-Wextra', 164 | '-Wpedantic', 165 | '-Wenum-conversion', 166 | '-Wextra-semi', 167 | '-Wnon-virtual-dtor', 168 | '-Wno-unused-parameter', 169 | ] 170 | __linkFlags = ['-Wl,-O1'] 171 | 172 | def getDebugFlags(self, conf): 173 | flags = super().getDebugFlags(conf) 174 | flags['CXXFLAGS'] += ['-Og', '-g'] + self.__cxxFlags + [ 175 | '-Werror', 176 | '-Wno-error=deprecated-declarations', # Bug #3795 177 | '-Wno-error=maybe-uninitialized', # Bug #1615 178 | ] 179 | flags['LINKFLAGS'] += self.__linkFlags 180 | # Enable assertions in libstdc++ 181 | # https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_macros.html 182 | flags['DEFINES'] += ['_GLIBCXX_ASSERTIONS=1'] 183 | return flags 184 | 185 | def getOptimizedFlags(self, conf): 186 | flags = super().getOptimizedFlags(conf) 187 | flags['CXXFLAGS'] += ['-O2', '-g1'] + self.__cxxFlags 188 | flags['LINKFLAGS'] += self.__linkFlags 189 | return flags 190 | 191 | 192 | class GccFlags(GccClangCommonFlags): 193 | __cxxFlags = [ 194 | '-Wcatch-value=2', 195 | '-Wcomma-subscript', # enabled by default in C++20 196 | '-Wduplicated-branches', 197 | '-Wduplicated-cond', 198 | '-Wlogical-op', 199 | '-Wredundant-tags', 200 | '-Wvolatile', # enabled by default in C++20 201 | ] 202 | 203 | def getDebugFlags(self, conf): 204 | flags = super().getDebugFlags(conf) 205 | flags['CXXFLAGS'] += self.__cxxFlags 206 | if platform.machine() == 'armv7l': 207 | flags['CXXFLAGS'] += ['-Wno-psabi'] # Bug #5106 208 | return flags 209 | 210 | def getOptimizedFlags(self, conf): 211 | flags = super().getOptimizedFlags(conf) 212 | flags['CXXFLAGS'] += self.__cxxFlags 213 | if platform.machine() == 'armv7l': 214 | flags['CXXFLAGS'] += ['-Wno-psabi'] # Bug #5106 215 | return flags 216 | 217 | 218 | class ClangFlags(GccClangCommonFlags): 219 | def getGeneralFlags(self, conf): 220 | flags = super().getGeneralFlags(conf) 221 | if Utils.unversioned_sys_platform() == 'darwin': 222 | # Bug #4296 223 | brewdir = '/opt/homebrew' if platform.machine() == 'arm64' else '/usr/local' 224 | flags['CXXFLAGS'] += [ 225 | ['-isystem', f'{brewdir}/include'], # for Homebrew 226 | ['-isystem', '/opt/local/include'], # for MacPorts 227 | ] 228 | else: 229 | if Utils.unversioned_sys_platform() == 'freebsd': 230 | # Bug #4790 231 | flags['CXXFLAGS'] += [['-isystem', '/usr/local/include']] 232 | if get_compiler_ver(conf) >= (18, 0, 0) and get_compiler_ver(conf) < (20, 1, 0): 233 | # Bug #5300 234 | flags['CXXFLAGS'] += ['-Wno-enum-constexpr-conversion'] 235 | return flags 236 | 237 | __cxxFlags = [ 238 | '-Wundefined-func-template', 239 | '-Wno-unused-local-typedef', # Bugs #2657 and #3209 240 | ] 241 | 242 | def getDebugFlags(self, conf): 243 | flags = super().getDebugFlags(conf) 244 | flags['CXXFLAGS'] += self.__cxxFlags 245 | flags['DEFINES'] += [ 246 | # Enable assertions in libc++ 247 | # https://libcxx.llvm.org/Hardening.html 248 | '_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE', 249 | # Disable transitive includes in libc++ 250 | # https://libcxx.llvm.org/DesignDocs/HeaderRemovalPolicy.html 251 | '_LIBCPP_REMOVE_TRANSITIVE_INCLUDES=1', 252 | ] 253 | return flags 254 | 255 | def getOptimizedFlags(self, conf): 256 | flags = super().getOptimizedFlags(conf) 257 | flags['CXXFLAGS'] += self.__cxxFlags 258 | return flags 259 | -------------------------------------------------------------------------------- /.waf-tools/doxygen.py: -------------------------------------------------------------------------------- 1 | # Thomas Nagy 2008-2010 (ita) 2 | 3 | """ 4 | Doxygen support 5 | 6 | Variables passed to bld(): 7 | * doxyfile -- the Doxyfile to use 8 | 9 | When using this tool, the wscript will look like: 10 | 11 | def options(opt): 12 | opt.load('doxygen') 13 | 14 | def configure(conf): 15 | conf.load('doxygen') 16 | # check conf.env.DOXYGEN, if it is mandatory 17 | 18 | def build(bld): 19 | if bld.env.DOXYGEN: 20 | bld(features="doxygen", doxyfile='Doxyfile', ...) 21 | 22 | def doxygen(bld): 23 | if bld.env.DOXYGEN: 24 | bld(features="doxygen", doxyfile='Doxyfile', ...) 25 | """ 26 | 27 | import os, os.path, re 28 | from waflib import Task, Utils, Node 29 | from waflib.TaskGen import feature 30 | 31 | DOXY_STR = '"${DOXYGEN}" - ' 32 | DOXY_FMTS = 'html latex man rft xml'.split() 33 | DOXY_FILE_PATTERNS = '*.' + ' *.'.join(''' 34 | c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3 35 | inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx 36 | '''.split()) 37 | 38 | re_rl = re.compile('\\\\\r*\n', re.MULTILINE) 39 | re_nl = re.compile('\r*\n', re.M) 40 | def parse_doxy(txt): 41 | tbl = {} 42 | txt = re_rl.sub('', txt) 43 | lines = re_nl.split(txt) 44 | for x in lines: 45 | x = x.strip() 46 | if not x or x.startswith('#') or x.find('=') < 0: 47 | continue 48 | if x.find('+=') >= 0: 49 | tmp = x.split('+=') 50 | key = tmp[0].strip() 51 | if key in tbl: 52 | tbl[key] += ' ' + '+='.join(tmp[1:]).strip() 53 | else: 54 | tbl[key] = '+='.join(tmp[1:]).strip() 55 | else: 56 | tmp = x.split('=') 57 | tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip() 58 | return tbl 59 | 60 | class doxygen(Task.Task): 61 | vars = ['DOXYGEN', 'DOXYFLAGS'] 62 | color = 'BLUE' 63 | 64 | def runnable_status(self): 65 | ''' 66 | self.pars are populated in runnable_status - because this function is being 67 | run *before* both self.pars "consumers" - scan() and run() 68 | 69 | set output_dir (node) for the output 70 | ''' 71 | 72 | for x in self.run_after: 73 | if not x.hasrun: 74 | return Task.ASK_LATER 75 | 76 | if not getattr(self, 'pars', None): 77 | txt = self.inputs[0].read() 78 | self.pars = parse_doxy(txt) 79 | if not self.pars.get('OUTPUT_DIRECTORY'): 80 | self.pars['OUTPUT_DIRECTORY'] = self.inputs[0].parent.get_bld().abspath() 81 | 82 | # Override with any parameters passed to the task generator 83 | if getattr(self.generator, 'pars', None): 84 | for k, v in self.generator.pars.items(): 85 | self.pars[k] = v 86 | 87 | self.doxy_inputs = getattr(self, 'doxy_inputs', []) 88 | if not self.pars.get('INPUT'): 89 | self.doxy_inputs.append(self.inputs[0].parent) 90 | else: 91 | for i in self.pars.get('INPUT').split(): 92 | if os.path.isabs(i): 93 | node = self.generator.bld.root.find_node(i) 94 | else: 95 | node = self.generator.path.find_node(i) 96 | if not node: 97 | self.generator.bld.fatal('Could not find the doxygen input %r' % i) 98 | self.doxy_inputs.append(node) 99 | 100 | if not getattr(self, 'output_dir', None): 101 | bld = self.generator.bld 102 | # First try to find an absolute path, then find or declare a relative path 103 | self.output_dir = bld.root.find_dir(self.pars['OUTPUT_DIRECTORY']) 104 | if not self.output_dir: 105 | self.output_dir = bld.path.find_or_declare(self.pars['OUTPUT_DIRECTORY']) 106 | 107 | self.signature() 108 | return Task.Task.runnable_status(self) 109 | 110 | def scan(self): 111 | exclude_patterns = self.pars.get('EXCLUDE_PATTERNS','').split() 112 | file_patterns = self.pars.get('FILE_PATTERNS','').split() 113 | if not file_patterns: 114 | file_patterns = DOXY_FILE_PATTERNS 115 | if self.pars.get('RECURSIVE') == 'YES': 116 | file_patterns = ["**/%s" % pattern for pattern in file_patterns] 117 | nodes = [] 118 | names = [] 119 | for node in self.doxy_inputs: 120 | if os.path.isdir(node.abspath()): 121 | for m in node.ant_glob(incl=file_patterns, excl=exclude_patterns): 122 | nodes.append(m) 123 | else: 124 | nodes.append(node) 125 | return (nodes, names) 126 | 127 | def run(self): 128 | dct = self.pars.copy() 129 | dct['INPUT'] = ' '.join(['"%s"' % x.abspath() for x in self.doxy_inputs]) 130 | code = '\n'.join(['%s = %s' % (x, dct[x]) for x in self.pars]) 131 | code = code.encode() # for python 3 132 | #fmt = DOXY_STR % (self.inputs[0].parent.abspath()) 133 | cmd = Utils.subst_vars(DOXY_STR, self.env) 134 | env = self.env.env or None 135 | proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE, env=env, cwd=self.generator.bld.path.get_bld().abspath()) 136 | proc.communicate(code) 137 | return proc.returncode 138 | 139 | def post_run(self): 140 | nodes = self.output_dir.ant_glob('**/*', quiet=True) 141 | for x in nodes: 142 | x.sig = Utils.h_file(x.abspath()) 143 | self.outputs += nodes 144 | return Task.Task.post_run(self) 145 | 146 | class tar(Task.Task): 147 | "quick tar creation" 148 | run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}' 149 | color = 'RED' 150 | after = ['doxygen'] 151 | def runnable_status(self): 152 | for x in getattr(self, 'input_tasks', []): 153 | if not x.hasrun: 154 | return Task.ASK_LATER 155 | 156 | if not getattr(self, 'tar_done_adding', None): 157 | # execute this only once 158 | self.tar_done_adding = True 159 | for x in getattr(self, 'input_tasks', []): 160 | self.set_inputs(x.outputs) 161 | if not self.inputs: 162 | return Task.SKIP_ME 163 | return Task.Task.runnable_status(self) 164 | 165 | def __str__(self): 166 | tgt_str = ' '.join([a.nice_path(self.env) for a in self.outputs]) 167 | return '%s: %s\n' % (self.__class__.__name__, tgt_str) 168 | 169 | @feature('doxygen') 170 | def process_doxy(self): 171 | if not getattr(self, 'doxyfile', None): 172 | self.generator.bld.fatal('no doxyfile??') 173 | 174 | node = self.doxyfile 175 | if not isinstance(node, Node.Node): 176 | node = self.path.find_resource(node) 177 | if not node: 178 | raise ValueError('doxygen file not found') 179 | 180 | # the task instance 181 | dsk = self.create_task('doxygen', node) 182 | 183 | if getattr(self, 'doxy_tar', None): 184 | tsk = self.create_task('tar') 185 | tsk.input_tasks = [dsk] 186 | tsk.set_outputs(self.path.find_or_declare(self.doxy_tar)) 187 | if self.doxy_tar.endswith('bz2'): 188 | tsk.env['TAROPTS'] = ['cjf'] 189 | elif self.doxy_tar.endswith('gz'): 190 | tsk.env['TAROPTS'] = ['czf'] 191 | else: 192 | tsk.env['TAROPTS'] = ['cf'] 193 | 194 | def configure(conf): 195 | ''' 196 | Check if doxygen and tar commands are present in the system 197 | 198 | If the commands are present, then conf.env.DOXYGEN and conf.env.TAR 199 | variables will be set. Detection can be controlled by setting DOXYGEN and 200 | TAR environmental variables. 201 | ''' 202 | 203 | conf.find_program('doxygen', var='DOXYGEN', mandatory=False) 204 | conf.find_program('tar', var='TAR', mandatory=False) 205 | 206 | # doxygen command 207 | from waflib.Build import BuildContext 208 | class doxy(BuildContext): 209 | cmd = 'doxygen' 210 | fun = 'doxygen' 211 | -------------------------------------------------------------------------------- /.waf-tools/sanitizers.py: -------------------------------------------------------------------------------- 1 | # Davide Pesavento (LIP6), 2016 2 | 3 | def options(opt): 4 | opt.add_option('--with-sanitizer', action='store', default='', dest='sanitizers', 5 | help='Comma-separated list of compiler sanitizers to enable [default=none]') 6 | 7 | def configure(conf): 8 | for san in conf.options.sanitizers.split(','): 9 | if not san: 10 | continue 11 | 12 | sanflag = '-fsanitize=%s' % san 13 | conf.start_msg('Checking if compiler supports %s' % sanflag) 14 | 15 | if conf.check_cxx(cxxflags=['-Werror', sanflag, '-fno-omit-frame-pointer'], 16 | linkflags=[sanflag], mandatory=False): 17 | conf.end_msg('yes') 18 | conf.env.append_unique('CXXFLAGS', [sanflag, '-fno-omit-frame-pointer']) 19 | conf.env.append_unique('LINKFLAGS', [sanflag]) 20 | else: 21 | conf.end_msg('no', color='RED') 22 | conf.fatal('%s sanitizer is not supported by the current compiler' % san) 23 | -------------------------------------------------------------------------------- /.waf-tools/sphinx.py: -------------------------------------------------------------------------------- 1 | # inspired by code by Hans-Martin von Gaudecker, 2012 2 | 3 | """Support for Sphinx documentation""" 4 | 5 | import os 6 | from waflib import Task, TaskGen 7 | 8 | 9 | class sphinx_build(Task.Task): 10 | color = 'BLUE' 11 | run_str = '${SPHINX_BUILD} -q -b ${BUILDERNAME} -D ${VERSION} -D ${RELEASE} -d ${DOCTREEDIR} ${SRCDIR} ${OUTDIR}' 12 | 13 | def keyword(self): 14 | return f'Processing ({self.env.BUILDERNAME})' 15 | 16 | 17 | # from https://docs.python.org/3.12/whatsnew/3.12.html#imp 18 | def load_source(modname, filename): 19 | import importlib.util 20 | from importlib.machinery import SourceFileLoader 21 | loader = SourceFileLoader(modname, filename) 22 | spec = importlib.util.spec_from_file_location(modname, filename, loader=loader) 23 | module = importlib.util.module_from_spec(spec) 24 | loader.exec_module(module) 25 | return module 26 | 27 | 28 | @TaskGen.feature('sphinx') 29 | @TaskGen.before_method('process_source') 30 | def process_sphinx(self): 31 | """Set up the task generator with a Sphinx instance and create a task.""" 32 | 33 | conf = self.path.find_node(self.config) 34 | if not conf: 35 | self.bld.fatal(f'Sphinx configuration file {repr(self.config)} not found') 36 | 37 | inputs = [conf] + self.to_nodes(self.source) 38 | task = self.create_task('sphinx_build', inputs, always_run=getattr(self, 'always', False)) 39 | 40 | confdir = conf.parent.abspath() 41 | buildername = getattr(self, 'builder', 'html') 42 | srcdir = getattr(self, 'srcdir', confdir) 43 | outdir = self.path.find_or_declare(getattr(self, 'outdir', buildername)).get_bld() 44 | doctreedir = getattr(self, 'doctreedir', os.path.join(outdir.abspath(), '.doctrees')) 45 | release = getattr(self, 'release', self.version) 46 | 47 | task.env['BUILDERNAME'] = buildername 48 | task.env['SRCDIR'] = srcdir 49 | task.env['OUTDIR'] = outdir.abspath() 50 | task.env['DOCTREEDIR'] = doctreedir 51 | task.env['VERSION'] = f'version={self.version}' 52 | task.env['RELEASE'] = f'release={release}' 53 | 54 | if buildername == 'man': 55 | confdata = load_source('sphinx_conf', conf.abspath()) 56 | for i in confdata.man_pages: 57 | target = outdir.find_or_declare(f'{i[1]}.{i[4]}') 58 | task.outputs.append(target) 59 | if self.install_path: 60 | self.bld.install_files(f'{self.install_path}/man{i[4]}/', target) 61 | else: 62 | task.outputs.append(outdir) 63 | 64 | # prevent process_source from complaining that there is no extension mapping for .rst files 65 | self.source = [] 66 | 67 | 68 | def configure(conf): 69 | """Check if sphinx-build program is available.""" 70 | conf.find_program('sphinx-build', var='SPHINX_BUILD', mandatory=False) 71 | 72 | 73 | # sphinx command 74 | from waflib.Build import BuildContext 75 | class sphinx(BuildContext): 76 | cmd = 'sphinx' 77 | fun = 'sphinx' 78 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # NAC Authors 2 | 3 | The following lists maintainers, primary developers, and all much-appreciated contributors to NAC in alphabetical order. 4 | The specific contributions of individual authors can be obtained from the git history of the [official NAC repository](https://github.com/named-data/name-based-access-control). 5 | If you would like to become a contributor to the official repository, please follow the recommendations in . 6 | 7 | * ***(Maintainer)*** Alexander Afanaysev 8 | * Ashlesh Gawande 9 | * Eric Newberry 10 | * Davide Pesavento 11 | * Prashanth Swaminathan 12 | * Yingdi Yu 13 | * Zhiyi Zhang 14 | -------------------------------------------------------------------------------- /COPYING.lesser: -------------------------------------------------------------------------------- 1 | # GNU LESSER GENERAL PUBLIC LICENSE 2 | 3 | Version 3, 29 June 2007 4 | 5 | Copyright (C) 2007 Free Software Foundation, Inc. 6 | 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this 9 | license document, but changing it is not allowed. 10 | 11 | This version of the GNU Lesser General Public License incorporates the 12 | terms and conditions of version 3 of the GNU General Public License, 13 | supplemented by the additional permissions listed below. 14 | 15 | ## 0. Additional Definitions. 16 | 17 | As used herein, "this License" refers to version 3 of the GNU Lesser 18 | General Public License, and the "GNU GPL" refers to version 3 of the 19 | GNU General Public License. 20 | 21 | "The Library" refers to a covered work governed by this License, other 22 | than an Application or a Combined Work as defined below. 23 | 24 | An "Application" is any work that makes use of an interface provided 25 | by the Library, but which is not otherwise based on the Library. 26 | Defining a subclass of a class defined by the Library is deemed a mode 27 | of using an interface provided by the Library. 28 | 29 | A "Combined Work" is a work produced by combining or linking an 30 | Application with the Library. The particular version of the Library 31 | with which the Combined Work was made is also called the "Linked 32 | Version". 33 | 34 | The "Minimal Corresponding Source" for a Combined Work means the 35 | Corresponding Source for the Combined Work, excluding any source code 36 | for portions of the Combined Work that, considered in isolation, are 37 | based on the Application, and not on the Linked Version. 38 | 39 | The "Corresponding Application Code" for a Combined Work means the 40 | object code and/or source code for the Application, including any data 41 | and utility programs needed for reproducing the Combined Work from the 42 | Application, but excluding the System Libraries of the Combined Work. 43 | 44 | ## 1. Exception to Section 3 of the GNU GPL. 45 | 46 | You may convey a covered work under sections 3 and 4 of this License 47 | without being bound by section 3 of the GNU GPL. 48 | 49 | ## 2. Conveying Modified Versions. 50 | 51 | If you modify a copy of the Library, and, in your modifications, a 52 | facility refers to a function or data to be supplied by an Application 53 | that uses the facility (other than as an argument passed when the 54 | facility is invoked), then you may convey a copy of the modified 55 | version: 56 | 57 | - a) under this License, provided that you make a good faith effort 58 | to ensure that, in the event an Application does not supply the 59 | function or data, the facility still operates, and performs 60 | whatever part of its purpose remains meaningful, or 61 | - b) under the GNU GPL, with none of the additional permissions of 62 | this License applicable to that copy. 63 | 64 | ## 3. Object Code Incorporating Material from Library Header Files. 65 | 66 | The object code form of an Application may incorporate material from a 67 | header file that is part of the Library. You may convey such object 68 | code under terms of your choice, provided that, if the incorporated 69 | material is not limited to numerical parameters, data structure 70 | layouts and accessors, or small macros, inline functions and templates 71 | (ten or fewer lines in length), you do both of the following: 72 | 73 | - a) Give prominent notice with each copy of the object code that 74 | the Library is used in it and that the Library and its use are 75 | covered by this License. 76 | - b) Accompany the object code with a copy of the GNU GPL and this 77 | license document. 78 | 79 | ## 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, taken 82 | together, effectively do not restrict modification of the portions of 83 | the Library contained in the Combined Work and reverse engineering for 84 | debugging such modifications, if you also do each of the following: 85 | 86 | - a) Give prominent notice with each copy of the Combined Work that 87 | the Library is used in it and that the Library and its use are 88 | covered by this License. 89 | - b) Accompany the Combined Work with a copy of the GNU GPL and this 90 | license document. 91 | - c) For a Combined Work that displays copyright notices during 92 | execution, include the copyright notice for the Library among 93 | these notices, as well as a reference directing the user to the 94 | copies of the GNU GPL and this license document. 95 | - d) Do one of the following: 96 | - 0) Convey the Minimal Corresponding Source under the terms of 97 | this License, and the Corresponding Application Code in a form 98 | suitable for, and under terms that permit, the user to 99 | recombine or relink the Application with a modified version of 100 | the Linked Version to produce a modified Combined Work, in the 101 | manner specified by section 6 of the GNU GPL for conveying 102 | Corresponding Source. 103 | - 1) Use a suitable shared library mechanism for linking with 104 | the Library. A suitable mechanism is one that (a) uses at run 105 | time a copy of the Library already present on the user's 106 | computer system, and (b) will operate properly with a modified 107 | version of the Library that is interface-compatible with the 108 | Linked Version. 109 | - e) Provide Installation Information, but only if you would 110 | otherwise be required to provide such information under section 6 111 | of the GNU GPL, and only to the extent that such information is 112 | necessary to install and execute a modified version of the 113 | Combined Work produced by recombining or relinking the Application 114 | with a modified version of the Linked Version. (If you use option 115 | 4d0, the Installation Information must accompany the Minimal 116 | Corresponding Source and Corresponding Application Code. If you 117 | use option 4d1, you must provide the Installation Information in 118 | the manner specified by section 6 of the GNU GPL for conveying 119 | Corresponding Source.) 120 | 121 | ## 5. Combined Libraries. 122 | 123 | You may place library facilities that are a work based on the Library 124 | side by side in a single library together with other library 125 | facilities that are not Applications and are not covered by this 126 | License, and convey such a combined library under terms of your 127 | choice, if you do both of the following: 128 | 129 | - a) Accompany the combined library with a copy of the same work 130 | based on the Library, uncombined with any other library 131 | facilities, conveyed under the terms of this License. 132 | - b) Give prominent notice with the combined library that part of it 133 | is a work based on the Library, and explaining where to find the 134 | accompanying uncombined form of the same work. 135 | 136 | ## 6. Revised Versions of the GNU Lesser General Public License. 137 | 138 | The Free Software Foundation may publish revised and/or new versions 139 | of the GNU Lesser General Public License from time to time. Such new 140 | versions will be similar in spirit to the present version, but may 141 | differ in detail to address new problems or concerns. 142 | 143 | Each version is given a distinguishing version number. If the Library 144 | as you received it specifies that a certain numbered version of the 145 | GNU Lesser General Public License "or any later version" applies to 146 | it, you have the option of following the terms and conditions either 147 | of that published version or of any later version published by the 148 | Free Software Foundation. If the Library as you received it does not 149 | specify a version number of the GNU Lesser General Public License, you 150 | may choose any version of the GNU Lesser General Public License ever 151 | published by the Free Software Foundation. 152 | 153 | If the Library as you received it specifies that a proxy can decide 154 | whether future versions of the GNU Lesser General Public License shall 155 | apply, that proxy's public statement of acceptance of any version is 156 | permanent authorization for you to choose that version for the 157 | Library. 158 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NAC: Name-Based Access Control Library for NDN 2 | 3 | ![Language](https://img.shields.io/badge/C%2B%2B-17-blue) 4 | [![CI](https://github.com/named-data/name-based-access-control/actions/workflows/ci.yml/badge.svg)](https://github.com/named-data/name-based-access-control/actions/workflows/ci.yml) 5 | [![Docs](https://github.com/named-data/name-based-access-control/actions/workflows/docs.yml/badge.svg)](https://github.com/named-data/name-based-access-control/actions/workflows/docs.yml) 6 | 7 | ## Reporting bugs 8 | 9 | Please submit any bug reports or feature requests to the 10 | [NAC issue tracker](https://redmine.named-data.net/projects/nac/issues). 11 | 12 | ## Contributing 13 | 14 | Contributions to NAC are greatly appreciated and can be made through our 15 | [Gerrit code review site](https://gerrit.named-data.net/). 16 | If you are new to the NDN software community, please read our [Contributor's Guide]( 17 | https://github.com/named-data/.github/blob/main/CONTRIBUTING.md) to get started. 18 | 19 | ## License 20 | 21 | NAC is free software distributed under the GNU Lesser General Public License version 3. 22 | See [`COPYING.md`](COPYING.md) and [`COPYING.lesser`](COPYING.lesser) for details. 23 | -------------------------------------------------------------------------------- /docs/_static/access-manager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/name-based-access-control/181a4de3256a86121d6980b334a7a40c4287fe60/docs/_static/access-manager.png -------------------------------------------------------------------------------- /docs/_static/decryptor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/name-based-access-control/181a4de3256a86121d6980b334a7a40c4287fe60/docs/_static/decryptor.png -------------------------------------------------------------------------------- /docs/_static/encryptor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/name-based-access-control/181a4de3256a86121d6980b334a7a40c4287fe60/docs/_static/encryptor.png -------------------------------------------------------------------------------- /docs/_static/nac-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/name-based-access-control/181a4de3256a86121d6980b334a7a40c4287fe60/docs/_static/nac-overview.png -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # For the full list of built-in configuration values, see the documentation: 4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 5 | 6 | import importlib.util 7 | import sys 8 | 9 | # -- Project information ----------------------------------------------------- 10 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 11 | 12 | project = 'NAC: Name-based Access Control library' 13 | copyright = 'Copyright © 2014-2024 Regents of the University of California.' 14 | author = 'Named Data Networking Project' 15 | 16 | # The short X.Y version. 17 | #version = '' 18 | 19 | # The full version, including alpha/beta/rc tags. 20 | #release = '' 21 | 22 | # There are two options for replacing |today|: either, you set today to some 23 | # non-false value, then it is used: 24 | #today = '' 25 | # Else, today_fmt is used as the format for a strftime call. 26 | today_fmt = '%Y-%m-%d' 27 | 28 | 29 | # -- General configuration --------------------------------------------------- 30 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 31 | 32 | needs_sphinx = '4.0' 33 | extensions = [ 34 | 'sphinx.ext.extlinks', 35 | 'sphinx.ext.todo', 36 | ] 37 | 38 | def addExtensionIfExists(extension: str): 39 | try: 40 | if importlib.util.find_spec(extension) is None: 41 | raise ModuleNotFoundError(extension) 42 | except (ImportError, ValueError): 43 | sys.stderr.write(f'WARNING: Extension {extension!r} not found. ' 44 | 'Some documentation may not build correctly.\n') 45 | else: 46 | extensions.append(extension) 47 | 48 | addExtensionIfExists('sphinxcontrib.doxylink') 49 | 50 | templates_path = ['_templates'] 51 | exclude_patterns = ['Thumbs.db', '.DS_Store'] 52 | 53 | 54 | # -- Options for HTML output ------------------------------------------------- 55 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 56 | 57 | html_theme = 'named_data_theme' 58 | html_theme_path = ['.'] 59 | 60 | # Add any paths that contain custom static files (such as style sheets) here, 61 | # relative to this directory. They are copied after the builtin static files, 62 | # so a file named "default.css" will overwrite the builtin "default.css". 63 | html_static_path = ['_static'] 64 | 65 | html_copy_source = False 66 | html_show_sourcelink = False 67 | 68 | # Disable syntax highlighting of code blocks by default. 69 | highlight_language = 'none' 70 | 71 | 72 | # -- Misc options ------------------------------------------------------------ 73 | 74 | doxylink = { 75 | 'nac': ('nac.tag', 'doxygen/'), 76 | } 77 | 78 | extlinks = { 79 | 'issue': ('https://redmine.named-data.net/issues/%s', 'issue #%s'), 80 | } 81 | -------------------------------------------------------------------------------- /docs/figures/access-manager.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/name-based-access-control/181a4de3256a86121d6980b334a7a40c4287fe60/docs/figures/access-manager.pptx -------------------------------------------------------------------------------- /docs/figures/decryptor.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/name-based-access-control/181a4de3256a86121d6980b334a7a40c4287fe60/docs/figures/decryptor.pptx -------------------------------------------------------------------------------- /docs/figures/encryptor.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/name-based-access-control/181a4de3256a86121d6980b334a7a40c4287fe60/docs/figures/encryptor.pptx -------------------------------------------------------------------------------- /docs/figures/nac-overview.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/name-based-access-control/181a4de3256a86121d6980b334a7a40c4287fe60/docs/figures/nac-overview.pptx -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | NAC: Name-Based Access Control 2 | ============================== 3 | 4 | .. toctree:: 5 | :hidden: 6 | :maxdepth: 3 7 | 8 | spec 9 | 10 | NAC is a C++ library providing Name-Based Access control primitives that 11 | can be used to implement content confidentiality in NDN applications. 12 | 13 | Please submit any bug reports or feature requests to the `NAC issue tracker 14 | `__. 15 | 16 | Documentation 17 | ------------- 18 | 19 | - :doc:`spec` 20 | - `API documentation (doxygen) `__ 21 | 22 | License 23 | ------- 24 | 25 | NAC is an open source project licensed under the LGPL version 3. For more information about 26 | the license, refer to `COPYING.md `__ 27 | and `COPYING.lesser `__. 28 | -------------------------------------------------------------------------------- /docs/named_data_theme/layout.html: -------------------------------------------------------------------------------- 1 | {# 2 | named_data_theme/layout.html 3 | ~~~~~~~~~~~~~~~~~ 4 | #} 5 | {% extends "basic/layout.html" %} 6 | 7 | {% block header %} 8 | 9 |
10 | 11 | 12 |
13 |
14 | 17 |
18 | 19 | 20 | 23 |
24 |
25 | 26 | {% endblock %} 27 | 28 | {% block content %} 29 |
30 |
31 |
32 | {%- block document %} 33 | {{ super() }} 34 | {%- endblock %} 35 |
36 | 60 |
61 |
62 |
63 | {% endblock %} 64 | 65 | {% block footer %} 66 | 71 | 72 | 82 | {% endblock %} 83 | 84 | {% block relbar1 %}{% endblock %} 85 | {% block relbar2 %}{% endblock %} 86 | -------------------------------------------------------------------------------- /docs/named_data_theme/named_data_footer-with-analytics.html.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /docs/named_data_theme/named_data_footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/named_data_theme/named_data_header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | $projectname: $title 7 | $title 8 | 9 | 10 | 11 | $treeview 12 | $search 13 | $mathjax 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 |
24 | 25 | 26 |
27 |
28 | 31 |
32 | 33 | 34 | 37 |
38 |
39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/named_data_theme/static/bar-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/name-based-access-control/181a4de3256a86121d6980b334a7a40c4287fe60/docs/named_data_theme/static/bar-top.png -------------------------------------------------------------------------------- /docs/named_data_theme/static/base.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0px; 3 | padding: 0px; 4 | } 5 | 6 | html { font-size: 62.5%; } 7 | 8 | body { 9 | font-family: "Verdana", Arial, sans-serif; 10 | background-color: #eeeeec; 11 | color: #777; 12 | border-top: 4px solid #fd7800; 13 | } 14 | 15 | body { background: white; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1; color: #222222; position: relative; -webkit-font-smoothing: antialiased; } 16 | 17 | .clearer { 18 | clear: both; 19 | } 20 | 21 | .left { 22 | float: left; 23 | } 24 | 25 | .right { 26 | float: right; 27 | } 28 | 29 | .line-block { 30 | display: block; 31 | margin-top: 1em; 32 | margin-bottom: 1em; 33 | } 34 | 35 | .line-block .line-block { 36 | margin-top: 0; 37 | margin-bottom: 0; 38 | margin-left: 1.5em; 39 | } 40 | 41 | h1, h2, h3, h4 { 42 | font-family: "Georgia", "Times New Roman", serif; 43 | font-weight: normal; 44 | color: #3465a4; 45 | margin-bottom: .8em; 46 | } 47 | 48 | h1 { 49 | color: #204a87; 50 | } 51 | 52 | h2 { 53 | padding-bottom: .5em; 54 | border-bottom: 1px solid #3465a4; 55 | } 56 | 57 | a.headerlink { 58 | visibility: hidden; 59 | color: #dddddd; 60 | padding-left: .3em; 61 | } 62 | 63 | h1:hover > a.headerlink, 64 | h2:hover > a.headerlink, 65 | h3:hover > a.headerlink, 66 | h4:hover > a.headerlink, 67 | h5:hover > a.headerlink, 68 | h6:hover > a.headerlink, 69 | dt:hover > a.headerlink { 70 | visibility: visible; 71 | } 72 | -------------------------------------------------------------------------------- /docs/named_data_theme/static/base.css_t: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0px; 3 | padding: 0px; 4 | } 5 | 6 | html { font-size: 62.5%; } 7 | 8 | body { 9 | font-family: {{ theme_bodyfont }}; 10 | background-color: {{ theme_bgcolor }}; 11 | color: #777; 12 | border-top: 4px solid #fd7800; 13 | } 14 | 15 | body { background: white; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1; color: #222222; position: relative; -webkit-font-smoothing: antialiased; } 16 | 17 | /* Page layout */ 18 | 19 | div.header, div.content, div.footer { 20 | width: 90%; 21 | margin-left: auto; 22 | margin-right: auto; 23 | } 24 | 25 | div.header-wrapper { 26 | background: {{ theme_headerbg }}; 27 | border-bottom: 3px solid #2e3436; 28 | } 29 | 30 | 31 | /* Default body styles */ 32 | a { 33 | color: {{ theme_linkcolor }}; 34 | } 35 | 36 | div.bodywrapper a, div.footer a { 37 | text-decoration: none; 38 | } 39 | 40 | .clearer { 41 | clear: both; 42 | } 43 | 44 | .left { 45 | float: left; 46 | } 47 | 48 | .right { 49 | float: right; 50 | } 51 | 52 | .line-block { 53 | display: block; 54 | margin-top: 1em; 55 | margin-bottom: 1em; 56 | } 57 | 58 | .line-block .line-block { 59 | margin-top: 0; 60 | margin-bottom: 0; 61 | margin-left: 1.5em; 62 | } 63 | 64 | h1, h2, h3, h4 { 65 | font-family: {{ theme_headerfont }}; 66 | font-weight: normal; 67 | color: {{ theme_headercolor2 }}; 68 | margin-bottom: .8em; 69 | } 70 | 71 | h1 { 72 | color: {{ theme_headercolor1 }}; 73 | } 74 | 75 | h2 { 76 | padding-bottom: .5em; 77 | border-bottom: 1px solid {{ theme_headercolor2 }}; 78 | } 79 | 80 | a.headerlink { 81 | visibility: hidden; 82 | color: #dddddd; 83 | padding-left: .3em; 84 | } 85 | 86 | h1:hover > a.headerlink, 87 | h2:hover > a.headerlink, 88 | h3:hover > a.headerlink, 89 | h4:hover > a.headerlink, 90 | h5:hover > a.headerlink, 91 | h6:hover > a.headerlink, 92 | dt:hover > a.headerlink { 93 | visibility: visible; 94 | } 95 | 96 | img { 97 | border: 0; 98 | } 99 | 100 | div.admonition { 101 | margin-top: 10px; 102 | margin-bottom: 10px; 103 | padding: 2px 7px 1px 7px; 104 | border-left: 0.2em solid black; 105 | } 106 | 107 | p.admonition-title { 108 | margin: 0px 10px 5px 0px; 109 | font-weight: bold; 110 | } 111 | 112 | dt:target, .highlighted { 113 | background-color: #fbe54e; 114 | } 115 | 116 | /* Header */ 117 | 118 | div.header { 119 | padding-top: 10px; 120 | padding-bottom: 10px; 121 | } 122 | 123 | div.header .headertitle { 124 | font-family: {{ theme_headerfont }}; 125 | font-weight: normal; 126 | font-size: 180%; 127 | letter-spacing: .08em; 128 | margin-bottom: .8em; 129 | } 130 | 131 | div.header .headertitle a { 132 | color: white; 133 | } 134 | 135 | div.header div.rel { 136 | margin-top: 1em; 137 | } 138 | 139 | div.header div.rel a { 140 | color: {{ theme_headerlinkcolor }}; 141 | letter-spacing: .1em; 142 | text-transform: uppercase; 143 | } 144 | 145 | p.logo { 146 | float: right; 147 | } 148 | 149 | img.logo { 150 | border: 0; 151 | } 152 | 153 | 154 | /* Content */ 155 | div.content-wrapper { 156 | background-color: white; 157 | padding-top: 20px; 158 | padding-bottom: 20px; 159 | } 160 | 161 | div.document { 162 | width: 70%; 163 | float: left; 164 | } 165 | 166 | div.body { 167 | padding-right: 2em; 168 | text-align: left; 169 | } 170 | 171 | div.document h1 { 172 | line-height: 120%; 173 | } 174 | 175 | div.document ul { 176 | margin-left: 1.5em; 177 | list-style-type: square; 178 | } 179 | 180 | div.document dd { 181 | margin-left: 1.2em; 182 | margin-top: .4em; 183 | margin-bottom: 1em; 184 | } 185 | 186 | div.document .section { 187 | margin-top: 1.7em; 188 | } 189 | div.document .section:first-child { 190 | margin-top: 0px; 191 | } 192 | 193 | div.document div.highlight { 194 | padding: 3px; 195 | background-color: #eeeeec; 196 | border-top: 2px solid #dddddd; 197 | border-bottom: 2px solid #dddddd; 198 | margin-bottom: .8em; 199 | } 200 | 201 | div.document h2 { 202 | margin-top: .7em; 203 | } 204 | 205 | div.document p { 206 | margin-bottom: .5em; 207 | } 208 | 209 | div.document li.toctree-l1 { 210 | margin-bottom: 1em; 211 | } 212 | 213 | div.document .descname { 214 | font-weight: bold; 215 | } 216 | 217 | div.document .docutils.literal { 218 | background-color: #eeeeec; 219 | padding: 1px; 220 | } 221 | 222 | div.document .docutils.xref.literal { 223 | background-color: transparent; 224 | padding: 0px; 225 | } 226 | 227 | div.document ol { 228 | margin: 1.5em; 229 | } 230 | 231 | 232 | /* Sidebar */ 233 | 234 | div.sidebar { 235 | width: 20%; 236 | float: right; 237 | font-size: .9em; 238 | } 239 | 240 | div.sidebar a, div.header a { 241 | text-decoration: none; 242 | } 243 | 244 | div.sidebar a:hover, div.header a:hover { 245 | text-decoration: none; 246 | } 247 | 248 | div.sidebar h3 { 249 | color: #2e3436; 250 | text-transform: uppercase; 251 | font-size: 130%; 252 | letter-spacing: .1em; 253 | } 254 | 255 | div.sidebar ul { 256 | list-style-type: none; 257 | } 258 | 259 | div.sidebar li.toctree-l1 a { 260 | display: block; 261 | padding: 1px; 262 | border: 1px solid #dddddd; 263 | background-color: #eeeeec; 264 | margin-bottom: .4em; 265 | padding-left: 3px; 266 | color: #2e3436; 267 | } 268 | 269 | div.sidebar li.toctree-l2 a { 270 | background-color: transparent; 271 | border: none; 272 | margin-left: 1em; 273 | border-bottom: 1px solid #dddddd; 274 | } 275 | 276 | div.sidebar li.toctree-l3 a { 277 | background-color: transparent; 278 | border: none; 279 | margin-left: 2em; 280 | border-bottom: 1px solid #dddddd; 281 | } 282 | 283 | div.sidebar li.toctree-l2:last-child a { 284 | border-bottom: none; 285 | } 286 | 287 | div.sidebar li.toctree-l1.current a { 288 | border-right: 5px solid {{ theme_headerlinkcolor }}; 289 | } 290 | 291 | div.sidebar li.toctree-l1.current li.toctree-l2 a { 292 | border-right: none; 293 | } 294 | 295 | div.sidebar input[type="text"] { 296 | width: 170px; 297 | } 298 | 299 | div.sidebar input[type="submit"] { 300 | width: 30px; 301 | } 302 | 303 | 304 | /* Footer */ 305 | 306 | div.footer-wrapper { 307 | background: {{ theme_footerbg }}; 308 | border-top: 4px solid #babdb6; 309 | padding-top: 10px; 310 | padding-bottom: 10px; 311 | min-height: 80px; 312 | } 313 | 314 | div.footer, div.footer a { 315 | color: #888a85; 316 | } 317 | 318 | div.footer .right { 319 | text-align: right; 320 | } 321 | 322 | div.footer .left { 323 | text-transform: uppercase; 324 | } 325 | 326 | 327 | /* Styles copied from basic theme */ 328 | 329 | img.align-left, .figure.align-left, object.align-left { 330 | clear: left; 331 | float: left; 332 | margin-right: 1em; 333 | } 334 | 335 | img.align-right, .figure.align-right, object.align-right { 336 | clear: right; 337 | float: right; 338 | margin-left: 1em; 339 | } 340 | 341 | img.align-center, .figure.align-center, object.align-center { 342 | display: block; 343 | margin-left: auto; 344 | margin-right: auto; 345 | } 346 | 347 | .align-left { 348 | text-align: left; 349 | } 350 | 351 | .align-center { 352 | text-align: center; 353 | } 354 | 355 | .align-right { 356 | text-align: right; 357 | } 358 | 359 | /* -- search page ----------------------------------------------------------- */ 360 | 361 | ul.search { 362 | margin: 10px 0 0 20px; 363 | padding: 0; 364 | } 365 | 366 | ul.search li { 367 | padding: 5px 0 5px 20px; 368 | background-image: url(file.png); 369 | background-repeat: no-repeat; 370 | background-position: 0 7px; 371 | } 372 | 373 | ul.search li a { 374 | font-weight: bold; 375 | } 376 | 377 | ul.search li div.context { 378 | color: #888; 379 | margin: 2px 0 0 30px; 380 | text-align: left; 381 | } 382 | 383 | ul.keywordmatches li.goodmatch a { 384 | font-weight: bold; 385 | } 386 | 387 | /* -- index page ------------------------------------------------------------ */ 388 | 389 | table.contentstable { 390 | width: 90%; 391 | } 392 | 393 | table.contentstable p.biglink { 394 | line-height: 150%; 395 | } 396 | 397 | a.biglink { 398 | font-size: 1.3em; 399 | } 400 | 401 | span.linkdescr { 402 | font-style: italic; 403 | padding-top: 5px; 404 | font-size: 90%; 405 | } 406 | 407 | /* -- general index --------------------------------------------------------- */ 408 | 409 | table.indextable td { 410 | text-align: left; 411 | vertical-align: top; 412 | } 413 | 414 | table.indextable dl, table.indextable dd { 415 | margin-top: 0; 416 | margin-bottom: 0; 417 | } 418 | 419 | table.indextable tr.pcap { 420 | height: 10px; 421 | } 422 | 423 | table.indextable tr.cap { 424 | margin-top: 10px; 425 | background-color: #f2f2f2; 426 | } 427 | 428 | img.toggler { 429 | margin-right: 3px; 430 | margin-top: 3px; 431 | cursor: pointer; 432 | } 433 | 434 | /* -- viewcode extension ---------------------------------------------------- */ 435 | 436 | .viewcode-link { 437 | float: right; 438 | } 439 | 440 | .viewcode-back { 441 | float: right; 442 | font-family:: {{ theme_bodyfont }}; 443 | } 444 | 445 | div.viewcode-block:target { 446 | margin: -1px -3px; 447 | padding: 0 3px; 448 | background-color: #f4debf; 449 | border-top: 1px solid #ac9; 450 | border-bottom: 1px solid #ac9; 451 | } 452 | 453 | td.linenos pre { 454 | padding: 5px 0px; 455 | border: 0; 456 | background-color: transparent; 457 | color: #aaa; 458 | margin-top: -10pt; 459 | } -------------------------------------------------------------------------------- /docs/named_data_theme/static/bc_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/name-based-access-control/181a4de3256a86121d6980b334a7a40c4287fe60/docs/named_data_theme/static/bc_s.png -------------------------------------------------------------------------------- /docs/named_data_theme/static/default.css_t: -------------------------------------------------------------------------------- 1 | @import url("agogo.css"); 2 | 3 | pre { 4 | padding: 10px; 5 | background-color: #fafafa; 6 | color: #222; 7 | line-height: 1.2em; 8 | border: 2px solid #C6C9CB; 9 | font-size: 1.1em; 10 | /* margin: 1.5em 0 1.5em 0; */ 11 | margin: 0; 12 | border-right-style: none; 13 | border-left-style: none; 14 | } 15 | -------------------------------------------------------------------------------- /docs/named_data_theme/static/named_data_doxygen.css: -------------------------------------------------------------------------------- 1 | @import url("base.css"); 2 | 3 | @import url("foundation.css"); 4 | 5 | table { 6 | border: 0; 7 | } 8 | 9 | pre { 10 | padding: 10px; 11 | background-color: #fafafa; 12 | color: #222; 13 | line-height: 1.0em; 14 | border: 2px solid #C6C9CB; 15 | font-size: 0.9em; 16 | /* margin: 1.5em 0 1.5em 0; */ 17 | margin: 0; 18 | border-right-style: none; 19 | border-left-style: none; 20 | } 21 | 22 | /* General */ 23 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 24 | 25 | a:link { 26 | text-decoration: none; 27 | } 28 | a:visited { 29 | text-decoration: none; 30 | } 31 | a:active, 32 | a:hover { 33 | text-decoration: none; 34 | } 35 | 36 | h1,h2,h3,h4,h5,h6 { 37 | color: #000; 38 | margin-bottom: 18px; 39 | } 40 | 41 | h1 { font-weight: normal; font-size: 24px; line-height: 24px; } 42 | h2 { font-weight: normal; font-size: 18px; line-height: 18px; } 43 | h3 { font-weight: bold; font-size: 18px; line-height: 18px; } 44 | h4 { font-weight: normal; font-size: 18px; line-height: 178px; } 45 | 46 | hr { 47 | background-color: #c6c6c6; 48 | border:0; 49 | height: 1px; 50 | margin-bottom: 18px; 51 | clear:both; 52 | } 53 | 54 | div.hr { 55 | height: 1px; 56 | background: #c6c6c6; 57 | } 58 | 59 | div.hr2 { 60 | height: 1px; 61 | background: #c6c6c6; 62 | } 63 | 64 | div.hr hr, div.hr2 hr { 65 | display: none; 66 | } 67 | 68 | p { 69 | padding: 0 0 0.5em; 70 | line-height:1.6em; 71 | } 72 | ul { 73 | list-style: square; 74 | margin: 0 0 18px 0; 75 | } 76 | ol { 77 | list-style: decimal; 78 | margin: 0 0 18px 1.5em; 79 | } 80 | ol ol { 81 | list-style:upper-alpha; 82 | } 83 | ol ol ol { 84 | list-style:lower-roman; 85 | } 86 | ol ol ol ol { 87 | list-style:lower-alpha; 88 | } 89 | ul ul, 90 | ol ol, 91 | ul ol, 92 | ol ul { 93 | margin-bottom:0; 94 | } 95 | dl { 96 | margin:0 0 24px 0; 97 | } 98 | dt { 99 | font-weight: bold; 100 | } 101 | dd { 102 | margin-bottom: 18px; 103 | } 104 | strong { 105 | font-weight: bold; 106 | color: #000; 107 | } 108 | cite, 109 | em, 110 | i { 111 | font-style: italic; 112 | border: none; 113 | } 114 | big { 115 | font-size: 131.25%; 116 | } 117 | ins { 118 | background: #FFFFCC; 119 | border: none; 120 | color: #333; 121 | } 122 | del { 123 | text-decoration: line-through; 124 | color: #555; 125 | } 126 | blockquote { 127 | font-style: italic; 128 | padding: 0 3em; 129 | } 130 | blockquote cite, 131 | blockquote em, 132 | blockquote i { 133 | font-style: normal; 134 | } 135 | pre { 136 | background: #f7f7f7; 137 | color: #222; 138 | padding: 1.5em; 139 | } 140 | abbr, 141 | acronym { 142 | border-bottom: 1px solid #666; 143 | cursor: help; 144 | } 145 | ins { 146 | text-decoration: none; 147 | } 148 | sup, 149 | sub { 150 | height: 0; 151 | line-height: 1; 152 | vertical-align: baseline; 153 | position: relative; 154 | font-size: 10px; 155 | } 156 | sup { 157 | bottom: 1ex; 158 | } 159 | sub { 160 | top: .5ex; 161 | } 162 | 163 | p, 164 | ul, 165 | ol, 166 | dd, 167 | hr { 168 | margin-bottom:10px; 169 | } 170 | ul ul, 171 | ol ol, 172 | ul ol, 173 | ol ul { 174 | margin-bottom:0; 175 | } 176 | pre, 177 | kbd, 178 | tt, 179 | var { 180 | } 181 | code { 182 | font-size: 13px; 183 | } 184 | strong, 185 | b, 186 | dt, 187 | th { 188 | color: #000; 189 | } 190 | 191 | 192 | /* main_container */ 193 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 194 | 195 | #wrapper { 196 | padding: 0px 0px; 197 | margin-top: 20px; 198 | } 199 | 200 | /* header*/ 201 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 202 | 203 | #search-header{ 204 | margin-top:15px; 205 | padding-bottom:13px; 206 | } 207 | 208 | #search-header #search{ 209 | background: #222; 210 | 211 | } 212 | 213 | #search-header #search #s{ 214 | background: #222; 215 | font-size:12px; 216 | color: #aaa; 217 | } 218 | 219 | #header_container{ 220 | padding-bottom: 25px; 221 | padding-top: 0px; 222 | background: #fff; 223 | } 224 | 225 | #header { 226 | 227 | } 228 | 229 | #header2 { 230 | 231 | } 232 | 233 | #content_container{ 234 | padding-top: 15px; 235 | } 236 | 237 | #left-col { 238 | padding: 10px 20px; 239 | padding-left: 0px; 240 | background: #fff; 241 | 242 | } 243 | 244 | 245 | /*footer*/ 246 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 247 | 248 | 249 | #footer { 250 | padding: 5px 20px; 251 | background: #ddd; 252 | } 253 | 254 | #footer-container{ 255 | padding: 5px 20px; 256 | background: #303030; 257 | border-top: 8px solid #000; 258 | font-size:11px; 259 | } 260 | 261 | #footer-info { 262 | color:#ccc; 263 | text-align:left; 264 | background: #1b1b1b; 265 | padding: 20px 0; 266 | } 267 | 268 | 269 | #footer-info a{ 270 | text-decoration:none; 271 | color: #fff; 272 | } 273 | 274 | #footer-info a:hover{ 275 | color: #ebebeb; 276 | } 277 | 278 | #copyright{float: left;} 279 | 280 | .scroll-top { 281 | text-align:right; 282 | } 283 | 284 | #footer-widget{ 285 | padding: 8px 0px 8px 0px; 286 | color:#6f6f6f; 287 | } 288 | 289 | #footer-widget #search { 290 | width:120px; 291 | height:28px; 292 | background: #222; 293 | margin-left: 0px; 294 | position: relative; 295 | border: 1px solid #666; 296 | } 297 | 298 | #footer-widget #search #s { 299 | width:110px; 300 | height:23px; 301 | border:0px; 302 | margin-left:7px; 303 | margin-right:10px; 304 | margin-top:3px; 305 | color:#fff; 306 | display: inline; 307 | background: #222; 308 | float: left; 309 | } 310 | 311 | #footer-widget #calendar_wrap { 312 | padding: 8px 0px; 313 | } 314 | 315 | #footer-widget #wp-calendar td{ 316 | padding:2px; 317 | } 318 | 319 | 320 | #footer-widget .textwidget { 321 | padding: 5px 0px; 322 | line-height: 23px; 323 | } 324 | 325 | 326 | #footer-widget .widget_tag_cloud a{ 327 | text-decoration: none; 328 | margin: 5px; 329 | line-height: 24px; 330 | margin-left: 0px; 331 | color: #6f6f6f; 332 | } 333 | 334 | #footer-widget .widget_tag_cloud a:hover{ 335 | color: #fff; 336 | } 337 | 338 | #footer-widget .widget-container ul li a { 339 | color:#fd7800; 340 | } 341 | 342 | #footer-widget .widget-container ul li a:hover { 343 | color: #ccc; 344 | } 345 | 346 | #footer-widget .widget-container h3 { 347 | color: #a5a5a5; 348 | text-transform: uppercase; 349 | margin-bottom: 0px; 350 | padding-top: 10px; 351 | padding-left: 0px; 352 | font-size: 25px; 353 | padding-bottom: 8px; 354 | font-weight: bold; 355 | } 356 | 357 | #footer-widget .widget-container ul li { 358 | padding: 5px 0px; 359 | background: none; 360 | } 361 | 362 | #footer-widget ul { 363 | margin-left: 0px; 364 | } 365 | 366 | #footer-bar1 { 367 | padding-right: 40px; 368 | } 369 | #footer-bar2 { 370 | padding-right: 40px; 371 | } 372 | #footer-bar3 { 373 | } 374 | #footer-bar4 { 375 | } 376 | 377 | span#follow-box{ 378 | position: absolute; 379 | right: 100px; 380 | } 381 | 382 | span#follow-box img{ 383 | margin: 0 2px; 384 | } 385 | 386 | /*logo*/ 387 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 388 | 389 | #logo { 390 | margin: 0px 0px 0px 0px; 391 | } 392 | 393 | #logo2 { 394 | margin: 0px 0px 0px 0px; 395 | } 396 | 397 | #logo img{ 398 | border: none; 399 | } 400 | 401 | #logo2{ 402 | text-decoration: none; 403 | font-size: 42px; 404 | letter-spacing: -1pt; 405 | font-weight: bold; 406 | font-family:arial, "Times New Roman", Times, serif; 407 | text-align: left; 408 | line-height: 57px; 409 | padding-left: 0px; 410 | } 411 | 412 | #logo2 a, #slogan{ 413 | color: #fd7800; 414 | } 415 | 416 | #slogan{ 417 | text-align: left; 418 | padding-left: 0px; 419 | } 420 | 421 | /*search*/ 422 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 423 | 424 | #search { 425 | width:180px; 426 | height:28px; 427 | border: 1px solid #ccc; 428 | margin-left: 10px; 429 | position: relative; 430 | } 431 | 432 | #sidebar #search { 433 | margin-top: 20px; 434 | } 435 | 436 | #search #searchsubmit { 437 | background:url(images/go-btn.png) no-repeat top right; 438 | width:28px; 439 | height:28px; 440 | border:0px; 441 | position:absolute; 442 | right: -35px; 443 | } 444 | 445 | #search #s { 446 | width:170px; 447 | height:23px; 448 | border:0px; 449 | margin-left:7px; 450 | margin-right:10px; 451 | margin-top:3px; 452 | color:#000; 453 | display: inline; 454 | float: left; 455 | } 456 | 457 | /*menu bar*/ 458 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 459 | 460 | #menu_container{ 461 | padding-top: 0px; 462 | } 463 | 464 | 465 | /*responsive menu*/ 466 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 467 | 468 | /* default style */ 469 | .selectnav { display: none; } 470 | 471 | /* small screen */ 472 | @media screen and (max-width: 600px) { 473 | .js #nav { display: none; } 474 | .js #nav2 { display: none; } 475 | .js .selectnav { display: block; } 476 | } 477 | 478 | 479 | /*welcome*/ 480 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 481 | #welcome_container h1{ 482 | margin-top: 0px; 483 | } 484 | 485 | /*homepage boxes*/ 486 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 487 | 488 | #box_container{ 489 | padding-top: 35px; 490 | padding-bottom: 15px; 491 | } 492 | 493 | .box-head { 494 | float: left; 495 | padding-bottom: 20px; 496 | } 497 | 498 | .box-head img{ 499 | 500 | } 501 | 502 | .title-head{ 503 | padding-top:2px; 504 | } 505 | 506 | .title-box{ 507 | color: #333; 508 | line-height: 15px; 509 | text-transform: uppercase; 510 | } 511 | 512 | .title-box h1 { 513 | font-size: 18px; 514 | margin-bottom: 3px; 515 | } 516 | 517 | .box-content { 518 | float: left; 519 | padding-top: 10px; 520 | line-height: 20px; 521 | } 522 | 523 | 524 | /* POST */ 525 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 526 | 527 | 528 | .post { 529 | overflow: hidden; 530 | 531 | } 532 | 533 | .post-shadow{ 534 | background: url("images/post_shadow.png") no-repeat bottom; 535 | height: 9px; 536 | margin-bottom: 25px; 537 | } 538 | 539 | .post ol{ 540 | margin-left: 20px; 541 | } 542 | 543 | .post ul { 544 | margin-left: 15px; 545 | } 546 | .post-entry ul { margin: 0 0 10px 10px; } 547 | .post-entry ul li { 548 | display: block; 549 | margin: 5px 0; 550 | padding: 0 0 0 20px; 551 | /*background: url(images/bullet.png) no-repeat 0 7px;*/ 552 | } 553 | 554 | .post-entry ol { 555 | list-style: decimal; 556 | margin: 0 0 18px 1.6em; 557 | } 558 | .post-entry ol li { 559 | list-style: decimal; 560 | } 561 | 562 | .post-entry { 563 | padding-bottom: 10px; 564 | padding-top: 10px; 565 | overflow: hidden; 566 | 567 | } 568 | 569 | .post-head { 570 | margin-bottom: 5px; 571 | padding-top: 15px; 572 | } 573 | 574 | .post-head h1 a, .post-head h1 { 575 | text-decoration:none; 576 | color:#000; 577 | margin: 0px; 578 | font-size: 27px; 579 | } 580 | 581 | .post-head h1 a:hover { 582 | color:#777; 583 | } 584 | 585 | 586 | .post-head-notfound h1, .post-head-404 h1, .post-head-archive h1, .post-head-search h1 { 587 | margin-bottom: 10px; 588 | font-weight:normal; 589 | text-decoration:none; 590 | color:#000; 591 | font-size: 27px; 592 | } 593 | 594 | .post-thumb img { 595 | border: 0px solid #ebebeb; 596 | } 597 | 598 | .post-entry img{ 599 | margin-bottom: 10px; 600 | height:auto; 601 | max-width:100% !important; 602 | } 603 | 604 | .meta-data{ 605 | line-height: 16px; 606 | padding: 6px 3px; 607 | margin-bottom: 3px; 608 | font-size: 11px; 609 | border-bottom: 1px solid #e9e9e9; 610 | } 611 | 612 | .meta-data a{ 613 | color: #fd7800; 614 | } 615 | 616 | .meta-data a:hover{ 617 | color: #777; 618 | } 619 | 620 | .read-more { 621 | color: #000; 622 | background: #fff; 623 | padding: 4px 8px; 624 | border-radius: 3px; 625 | display: inline-block; 626 | font-size: 11px; 627 | font-weight: bold; 628 | text-decoration: none; 629 | text-transform: capitalize; 630 | cursor: pointer; 631 | margin-top: 20px; 632 | } 633 | 634 | .read-more:hover{ 635 | background: #fff; 636 | color: #666; 637 | } 638 | 639 | .clear { 640 | clear:both; 641 | } 642 | 643 | .sticky { 644 | 645 | } 646 | 647 | /* content */ 648 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 649 | #content_container table { 650 | border: 1px solid #e7e7e7; 651 | margin: 0 -1px 24px 0; 652 | text-align: left; 653 | width: 100%; 654 | 655 | } 656 | #content_container tr th, 657 | #content_container thead th { 658 | color: #888; 659 | font-size: 12px; 660 | font-weight: bold; 661 | line-height: 18px; 662 | padding: 9px 10px; 663 | } 664 | #content_container tr td { 665 | 666 | padding: 6px 10px; 667 | } 668 | #content_container tr.odd td { 669 | background: #f2f7fc; 670 | } 671 | 672 | /*--navigation--*/ 673 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 674 | 675 | .navigation { 676 | float: left; 677 | width: 100%; 678 | margin: 20px 0; 679 | } 680 | 681 | 682 | .navigation .alignleft a { 683 | float: left; 684 | } 685 | 686 | .navigation .alignright a { 687 | float: right; 688 | } 689 | 690 | #nav-single { 691 | overflow:hidden; 692 | margin-top:20px; 693 | margin-bottom:10px; 694 | } 695 | .nav-previous { 696 | float: left; 697 | width: 50%; 698 | } 699 | .nav-next { 700 | float: right; 701 | text-align: right; 702 | width: 50%; 703 | } 704 | 705 | /*--sub head and breadcrumbs--*/ 706 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 707 | 708 | #subhead_container{ 709 | padding: 7px 0px; 710 | } 711 | 712 | #subhead h1{ 713 | color: #000; 714 | padding-top: 10px; 715 | padding-left: 0px; 716 | font-size: 30px; 717 | } 718 | 719 | #breadcrumbs { 720 | padding-left: 25px; 721 | margin-bottom: 15px; 722 | color: #9e9e9e; 723 | margin:0 auto; 724 | width: 964px; 725 | font-size: 10px; 726 | } 727 | 728 | #breadcrumbs a{ 729 | text-decoration: none; 730 | color: #9e9e9e; 731 | } 732 | 733 | /*Alignments */ 734 | /*////////////////////////////////////////////////////////////////////////////////////////////*/ 735 | 736 | .alignleft, 737 | img.alignleft { 738 | display: inline; 739 | float: left; 740 | margin-right: 22px; 741 | margin-top: 9px; 742 | } 743 | 744 | .alignright, 745 | img.alignright { 746 | display: inline; 747 | float: right; 748 | margin-left: 22px; 749 | margin-top: 8px; 750 | } 751 | .aligncenter, 752 | img.aligncenter { 753 | clear: both; 754 | display: block; 755 | margin-left: auto; 756 | margin-right: auto; 757 | } 758 | 759 | .alignleft, 760 | .alignright, 761 | .aligncenter, 762 | img.alignleft, 763 | img.alignright, 764 | img.aligncenter 765 | { 766 | margin-bottom: 10px; 767 | } 768 | 769 | 770 | a img.aligncenter { 771 | display:block; 772 | margin-left:auto; 773 | margin-right:auto; 774 | } 775 | 776 | img { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } 777 | -------------------------------------------------------------------------------- /docs/named_data_theme/static/nav_f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/name-based-access-control/181a4de3256a86121d6980b334a7a40c4287fe60/docs/named_data_theme/static/nav_f.png -------------------------------------------------------------------------------- /docs/named_data_theme/static/tab_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/name-based-access-control/181a4de3256a86121d6980b334a7a40c4287fe60/docs/named_data_theme/static/tab_b.png -------------------------------------------------------------------------------- /docs/named_data_theme/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = agogo 3 | stylesheet = named_data_style.css 4 | # pygments_style = sphinx 5 | 6 | theme_bodyfont = "normal 12px Verdana, sans-serif" 7 | theme_bgcolor = "#ccc" 8 | 9 | theme_documentwidth = "100%" 10 | theme_textalign = "left" 11 | 12 | [options] 13 | 14 | stickysidebar = true 15 | collapsiblesidebar = true 16 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | docutils>=0.20 2 | sphinx>=7.0.1,<9 3 | sphinxcontrib-doxylink~=1.13 4 | -------------------------------------------------------------------------------- /docs/spec.rst: -------------------------------------------------------------------------------- 1 | NAC Specification 2 | ================= 3 | 4 | .. figure:: _static/nac-overview.png 5 | :alt: Overview of NAC entities 6 | :align: center 7 | 8 | Terminology 9 | ----------- 10 | 11 | +-----------------+------------------------------------------------------------------------------------------+ 12 | | Term | Description | 13 | +=================+==========================================================================================+ 14 | | KEK | Key Encryption Key (RSA public key) | 15 | +-----------------+------------------------------------------------------------------------------------------+ 16 | | KDK | Key Decryption Key (RSA private key) | 17 | +-----------------+------------------------------------------------------------------------------------------+ 18 | | CK | Content Key (AES symmetric key) | 19 | +-----------------+------------------------------------------------------------------------------------------+ 20 | | CK data | Data packet carrying a KDK-encrypted CK as payload | 21 | +-----------------+------------------------------------------------------------------------------------------+ 22 | | Access Manager | (Data Owner) Entity that control access to the data associated with the namespace | 23 | +-----------------+------------------------------------------------------------------------------------------+ 24 | | Encryptor | (Producer) Entity that encrypts data based on namespace association | 25 | +-----------------+------------------------------------------------------------------------------------------+ 26 | | Decryptor | (Consumer) Entity that decrypts data based on namespace association | 27 | +-----------------+------------------------------------------------------------------------------------------+ 28 | 29 | EncryptedContent 30 | ---------------- 31 | 32 | The ``EncryptedContent`` element contains encrypted blob, optional Initialization Vector (for AES CBC encryption), 33 | optional EncryptedPayloadKey, and Name elements. 34 | 35 | .. code-block:: abnf 36 | 37 | EncryptedContent = ENCRYPTED-CONTENT-TYPE TLV-LENGTH 38 | EncryptedPayload 39 | [InitializationVector] 40 | [EncryptedPayloadKey] 41 | [Name] 42 | 43 | EncryptedPayload = ENCRYPTED-PAYLOAD-TYPE TLV-LENGTH *OCTET 44 | InitializationVector = INITIALIZATION-VECTOR-TYPE TLV-LENGTH *OCTET 45 | EncryptedPayloadKey = ENCRYPTED-PAYLOAD-KEY-TYPE TLV-LENGTH *OCTET 46 | 47 | Access Manager 48 | -------------- 49 | 50 | .. figure:: _static/access-manager.png 51 | :alt: Access Manager 52 | :align: center 53 | 54 | Access Manager controls decryption policy by publishing granular per-namespace access policies in the form of key encryption (KEK, plaintext public) and key decryption (KDK, encrypted private key) key pair. 55 | 56 | KEK is published as a single data packet with name ``/[access-namespace]/NAC/[dataset]/KEK/[key-id]``, following the following format: 57 | 58 | .. code-block:: abnf 59 | 60 | Kek = DATA-TYPE TLV-LENGTH 61 | Name ; /[access-namespace]/NAC/[dataset]/KEK/[key-id] 62 | MetaInfo ; ContentType = KEY, FreshnessPeriod = 1 hour default value 63 | KekContent 64 | DataSignature 65 | 66 | KekContent = CONTENT-TYPE-TLV TLV-LENGTH 67 | *OCTET ; = BER of public key /[access-namespace]/NAC/[dataset]/KEY/[key-id] 68 | 69 | Different versions of KDK are published, encrypted by the public key of the individual authorized member, following naming convention: ``/[access-namespace]/NAC/[dataset]/KDK/[key-id]/ENCRYPTED-BY//KEY/[member-key-id]``. KDK is published in the following format: 70 | 71 | .. code-block:: abnf 72 | 73 | Kdk = DATA-TYPE TLV-LENGTH 74 | Name ; /[access-namespace]/NAC/[dataset]/KDK/[key-id]/ENCRYPTED-BY//KEY/[member-key-id] 75 | MetaInfo ; ContentType = BLOB, FreshnessPeriod = 1 hour default value 76 | KdkContent 77 | DataSignature 78 | 79 | KdkContent = CONTENT-TYPE-TLV TLV-LENGTH 80 | EncryptedContent 81 | 82 | Within the ``EncryptedContent`` element, 83 | 84 | * ``EncryptedPayload`` contains `SafeBag `__ of private key ``/[access-namespace]/NAC/[dataset]/KEY/[key-id]`` 85 | * ``EncryptedPayloadKey`` contains password for SafeBag, encrypted by public key ``//KEY/[member-key-id]`` 86 | * ``InitializationVector`` and ``Name`` must be omitted 87 | 88 | Encryptor 89 | --------- 90 | 91 | .. figure:: _static/encryptor.png 92 | :alt: Encryptor 93 | :align: center 94 | 95 | Encryptor encrypts (synchronous operation) the requested content and returns an ``EncryptedContent`` element with values: 96 | 97 | :: 98 | 99 | EncryptedPayload = AES CBC encrypted blob 100 | InitializationVector = Random initial vector for AES CBC encryption 101 | EncryptedPayloadKey (not set) 102 | Name = Prefix of ContentKey (CK) data packet /[ck-prefix]/CK/[ck-id] 103 | 104 | During initialization or when requested by the application, the Encryptor (re-)generates a random key for AES CBC encryption. 105 | The encrypted version of this key is published (asynchronous operation, contingent on successful retrieval and validation of KEK) as a data packet, following the naming convention: ``/[ck-prefix]/CK/[ck-id]/ENCRYPTED-BY/[access-namespace]/NAC/[dataset]/KEK/[key-id]``. CK data is published in the following format: 106 | 107 | .. code-block:: abnf 108 | 109 | CkData = DATA-TYPE TLV-LENGTH 110 | Name ; /[ck-prefix]/CK/[ck-id]/ENCRYPTED-BY/[access-namespace]/NAC/[dataset]/KEK/[key-id] 111 | MetaInfo ; ContentType = BLOB, FreshnessPeriod = 1 hour default value 112 | CkContent 113 | DataSignature 114 | 115 | CkContent = CONTENT-TYPE-TLV TLV-LENGTH 116 | EncryptedContent 117 | 118 | Within the ``EncryptedContent`` element, 119 | 120 | * ``EncryptedPayload`` contains ContentKey encrypted by public key ``/[access-namespace]/NAC/[dataset]/KEK/[key-id]`` 121 | * ``EncryptedPayloadKey``, ``InitializationVector``, and ``Name`` must be omitted 122 | 123 | Decryptor 124 | --------- 125 | 126 | .. figure:: _static/decryptor.png 127 | :alt: Decryptor 128 | :align: center 129 | 130 | Encryptor decrypts (asynchronous operation, contingent on successful retrieval of CK data, KDK, and decryption of both) the supplied ``EncryptedContent`` element. 131 | 132 | TLV-TYPE number assignments 133 | --------------------------- 134 | 135 | +----------------------------------------+------------------+------------------+ 136 | | Type | Assigned number | Assigned number | 137 | | | (decimal) | (hexadecimal) | 138 | +========================================+==================+==================+ 139 | | EncryptedContent | 130 | 0x82 | 140 | +----------------------------------------+------------------+------------------+ 141 | | EncryptedPayload | 132 | 0x84 | 142 | +----------------------------------------+------------------+------------------+ 143 | | InitializationVector | 133 | 0x85 | 144 | +----------------------------------------+------------------+------------------+ 145 | | EncryptedPayloadKey | 134 | 0x86 | 146 | +----------------------------------------+------------------+------------------+ 147 | -------------------------------------------------------------------------------- /examples/consumer.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2023, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | // #include 24 | #include "decryptor.hpp" 25 | 26 | #include 27 | 28 | // Enclosing code in ndn simplifies coding (can also use `using namespace ndn`) 29 | namespace ndn { 30 | namespace nac { 31 | // Additional nested namespaces should be used to prevent/limit name conflicts 32 | namespace examples { 33 | 34 | class Consumer 35 | { 36 | public: 37 | Consumer() 38 | : m_decryptor(m_keyChain.getPib().getDefaultIdentity().getDefaultKey(), 39 | m_validator, m_keyChain, m_face) 40 | { 41 | m_validator.load(R"CONF( 42 | trust-anchor 43 | { 44 | type any 45 | } 46 | )CONF", "fake-config"); 47 | } 48 | 49 | void 50 | run() 51 | { 52 | using namespace std::placeholders; 53 | 54 | Interest interest(Name("/example/testApp/randomData")); 55 | interest.setCanBePrefix(true); 56 | interest.setMustBeFresh(true); 57 | interest.setInterestLifetime(3_s); // 3 seconds 58 | 59 | std::cout << "Sending Interest " << interest << std::endl; 60 | m_face.expressInterest(interest, 61 | std::bind(&Consumer::onData, this, _1, _2), 62 | std::bind(&Consumer::onNack, this, _1, _2), 63 | std::bind(&Consumer::onTimeout, this, _1)); 64 | 65 | // processEvents will block until the requested data is received or a timeout occurs 66 | m_face.processEvents(); 67 | } 68 | 69 | private: 70 | void 71 | onData(const Interest&, const Data& data) 72 | { 73 | m_validator.validate(data, 74 | [=] (const Data& data) { 75 | m_decryptor.decrypt(data.getContent().blockFromValue(), 76 | [] (const ConstBufferPtr& content) { 77 | std::cout << "Decrypted content: " 78 | << std::string(reinterpret_cast(content->data()), content->size()) 79 | << std::endl; 80 | }, 81 | [] (const ErrorCode&, const std::string& error) { 82 | std::cerr << "Cannot decrypt data: " << error << std::endl; 83 | }); 84 | }, 85 | [] (const Data&, const ValidationError& error) { 86 | std::cerr << "Cannot validate retrieved data: " << error << std::endl; 87 | }); 88 | } 89 | 90 | void 91 | onNack(const Interest& interest, const lp::Nack& nack) const 92 | { 93 | std::cout << "Received Nack with reason " << nack.getReason() 94 | << " for interest " << interest << std::endl; 95 | } 96 | 97 | void 98 | onTimeout(const Interest& interest) const 99 | { 100 | std::cout << "Timeout for " << interest << std::endl; 101 | } 102 | 103 | private: 104 | KeyChain m_keyChain; 105 | Face m_face{nullptr, m_keyChain}; 106 | ValidatorConfig m_validator{m_face}; 107 | Decryptor m_decryptor; 108 | }; 109 | 110 | } // namespace examples 111 | } // namespace nac 112 | } // namespace ndn 113 | 114 | int 115 | main(int argc, char** argv) 116 | { 117 | try { 118 | ndn::nac::examples::Consumer consumer; 119 | consumer.run(); 120 | return 0; 121 | } 122 | catch (const std::exception& e) { 123 | std::cerr << "ERROR: " << e.what() << std::endl; 124 | return 1; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /examples/producer.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2023, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "encryptor.hpp" 26 | #include "access-manager.hpp" 27 | 28 | #include 29 | 30 | // Enclosing code in ndn simplifies coding (can also use `using namespace ndn`) 31 | namespace ndn { 32 | namespace nac { 33 | // Additional nested namespaces should be used to prevent/limit name conflicts 34 | namespace examples { 35 | 36 | class Producer 37 | { 38 | public: 39 | Producer() 40 | : m_accessManager(m_keyChain.createIdentity("/nac/example", RsaKeyParams()), "test", 41 | m_keyChain, m_face) 42 | , m_encryptor("/nac/example/NAC/test", 43 | "/nac/example/CK", signingWithSha256(), 44 | [] (auto&&...) { std::cerr << "Failed to publish CK"; }, 45 | m_validator, m_keyChain, m_face) 46 | { 47 | m_validator.load(R"CONF( 48 | trust-anchor 49 | { 50 | type any 51 | } 52 | )CONF", "fake-config"); 53 | } 54 | 55 | void 56 | run() 57 | { 58 | // Give access to default identity. If consumer uses the same default identity, it will be able to decrypt 59 | m_accessManager.addMember(m_keyChain.getPib().getDefaultIdentity().getDefaultKey().getDefaultCertificate()); 60 | 61 | m_face.setInterestFilter("/example/testApp", 62 | std::bind(&Producer::onInterest, this, std::placeholders::_2), 63 | nullptr, // RegisterPrefixSuccessCallback is optional 64 | [this] (const Name& prefix, const std::string& reason) { 65 | std::cerr << "ERROR: Failed to register prefix '" << prefix 66 | << "' with the local forwarder (" << reason << ")\n"; 67 | m_face.shutdown(); 68 | }); 69 | m_face.processEvents(); 70 | } 71 | 72 | private: 73 | void 74 | onInterest(const Interest& interest) 75 | { 76 | std::cout << ">> I: " << interest << std::endl; 77 | 78 | // Create a new name for the Data packet, based on the Interest's name 79 | Name dataName(interest.getName()); 80 | dataName 81 | .append("testApp") // add "testApp" component to Interest name 82 | .appendVersion(); // add version component (current UNIX timestamp in milliseconds) 83 | 84 | // Create Data packet 85 | auto data = std::make_shared(); 86 | data->setName(dataName); 87 | data->setFreshnessPeriod(10_s); // 10 seconds 88 | 89 | constexpr std::string_view content{"Hello, world!"}; 90 | auto blob = m_encryptor.encrypt({reinterpret_cast(content.data()), content.size()}); 91 | data->setContent(blob.wireEncode()); 92 | 93 | // Sign Data packet with default identity 94 | m_keyChain.sign(*data); 95 | // m_keyChain.sign(*data, signingByIdentity()); 96 | // m_keyChain.sign(*data, signingByKey()); 97 | // m_keyChain.sign(*data, signingByCertificate()); 98 | 99 | // Return Data packet to the requester 100 | std::cout << "<< D: " << *data << std::endl; 101 | m_face.put(*data); 102 | } 103 | 104 | private: 105 | KeyChain m_keyChain; 106 | Face m_face{nullptr, m_keyChain}; 107 | ValidatorConfig m_validator{m_face}; 108 | AccessManager m_accessManager; 109 | Encryptor m_encryptor; 110 | }; 111 | 112 | } // namespace examples 113 | } // namespace nac 114 | } // namespace ndn 115 | 116 | int 117 | main(int argc, char** argv) 118 | { 119 | try { 120 | ndn::nac::examples::Producer producer; 121 | producer.run(); 122 | return 0; 123 | } 124 | catch (const std::exception& e) { 125 | std::cerr << "ERROR: " << e.what() << std::endl; 126 | return 1; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /examples/wscript: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- 2 | 3 | top = '..' 4 | 5 | def build(bld): 6 | # List all .cpp files (whole example in one .cpp) 7 | for ex in bld.path.ant_glob('*.cpp'): 8 | name = ex.change_ext('').path_from(bld.path.get_bld()) 9 | bld.program(name=f'example-{name}', 10 | target=f'nac-{name}', 11 | source=[ex], 12 | use='libndn-nac', 13 | install_path=None) 14 | 15 | # List all directories (example can have multiple .cpp in the directory) 16 | for subdir in bld.path.ant_glob('*', dir=True, src=False): 17 | name = subdir.path_from(bld.path) 18 | bld.program(name=f'example-{name}', 19 | target=name, 20 | source=subdir.ant_glob('**/*.cpp'), 21 | use='libndn-nac', 22 | includes=name, 23 | install_path=None) 24 | -------------------------------------------------------------------------------- /libndn-nac.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@PREFIX@ 2 | libdir=@LIBDIR@ 3 | includedir=@INCLUDEDIR@ 4 | 5 | Name: libndn-nac 6 | Description: NDN Name-Based Access Control library 7 | Version: @VERSION@ 8 | Libs: -L${libdir} -lndn-nac 9 | Cflags: -I${includedir} 10 | -------------------------------------------------------------------------------- /src/access-manager.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include "access-manager.hpp" 21 | #include "encrypted-content.hpp" 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | namespace ndn::nac { 28 | 29 | NDN_LOG_INIT(nac.AccessManager); 30 | 31 | AccessManager::AccessManager(const Identity& identity, const Name& dataset, 32 | KeyChain& keyChain, Face& face) 33 | : m_identity(identity) 34 | , m_keyChain(keyChain) 35 | , m_face(face) 36 | { 37 | // NAC Identity: /NAC/ 38 | // generate NAC key 39 | auto nacId = m_keyChain.createIdentity(Name(identity.getName()).append(NAC).append(dataset), RsaKeyParams()); 40 | m_nacKey = nacId.getDefaultKey(); 41 | if (m_nacKey.getKeyType() != KeyType::RSA) { 42 | NDN_LOG_INFO("Cannot re-use existing KEK/KDK pair, as it is not an RSA key, regenerating"); 43 | m_nacKey = m_keyChain.createKey(nacId, RsaKeyParams()); 44 | } 45 | auto nacKeyId = m_nacKey.getName().at(-1); 46 | 47 | auto kekPrefix = Name(m_nacKey.getIdentity()).append(KEK); 48 | 49 | auto kek = std::make_shared(m_nacKey.getDefaultCertificate()); 50 | kek->setName(Name(kekPrefix).append(nacKeyId)); 51 | kek->setFreshnessPeriod(DEFAULT_KEK_FRESHNESS_PERIOD); 52 | m_keyChain.sign(*kek, signingByIdentity(m_identity)); 53 | // kek looks like a cert, but doesn't have ValidityPeriod 54 | m_ims.insert(*kek); 55 | 56 | auto serveFromIms = [this] (const Name&, const Interest& interest) { 57 | auto data = m_ims.find(interest); 58 | if (data != nullptr) { 59 | NDN_LOG_DEBUG("Serving " << data->getName() << " from InMemoryStorage"); 60 | m_face.put(*data); 61 | } 62 | else { 63 | NDN_LOG_DEBUG("Didn't find data for " << interest.getName()); 64 | // send NACK? 65 | } 66 | }; 67 | 68 | auto handleError = [] (const Name& prefix, const std::string& msg) { 69 | NDN_LOG_ERROR("Failed to register prefix " << prefix << ": " << msg); 70 | }; 71 | 72 | m_kekReg = m_face.setInterestFilter(kekPrefix, serveFromIms, handleError); 73 | 74 | auto kdkPrefix = Name(m_nacKey.getIdentity()).append(KDK).append(nacKeyId); 75 | m_kdkReg = m_face.setInterestFilter(kdkPrefix, serveFromIms, handleError); 76 | } 77 | 78 | Data 79 | AccessManager::addMember(const Certificate& memberCert) 80 | { 81 | Name kdkName(m_nacKey.getIdentity()); 82 | kdkName 83 | .append(KDK) 84 | .append(m_nacKey.getName().at(-1)) // key-id 85 | .append(ENCRYPTED_BY) 86 | .append(memberCert.getKeyName()); 87 | 88 | const size_t secretLength = 32; 89 | uint8_t secret[secretLength + 1]; 90 | random::generateSecureBytes({secret, secretLength}); 91 | // because of stupid bug in ndn-cxx, remove all \0 in generated secret, replace with 1 92 | for (size_t i = 0; i < secretLength; ++i) { 93 | if (secret[i] == 0) { 94 | secret[i] = 1; 95 | } 96 | } 97 | secret[secretLength] = 0; 98 | 99 | auto kdkData = m_keyChain.exportSafeBag(m_nacKey.getDefaultCertificate(), 100 | reinterpret_cast(secret), secretLength); 101 | 102 | PublicKey memberKey; 103 | memberKey.loadPkcs8(memberCert.getPublicKey()); 104 | 105 | EncryptedContent content; 106 | content.setPayload(kdkData->wireEncode()); 107 | content.setPayloadKey(memberKey.encrypt({secret, secretLength})); 108 | 109 | auto kdk = std::make_shared(kdkName); 110 | kdk->setContent(content.wireEncode()); 111 | // FreshnessPeriod can serve as a soft access control for revoking access 112 | kdk->setFreshnessPeriod(DEFAULT_KDK_FRESHNESS_PERIOD); 113 | m_keyChain.sign(*kdk, signingByIdentity(m_identity)); 114 | 115 | m_ims.insert(*kdk); 116 | 117 | return *kdk; 118 | } 119 | 120 | void 121 | AccessManager::removeMember(const Name& identity) 122 | { 123 | m_ims.erase(Name(m_nacKey.getName()).append(KDK).append(ENCRYPTED_BY).append(identity)); 124 | } 125 | 126 | } // namespace ndn::nac 127 | -------------------------------------------------------------------------------- /src/access-manager.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #ifndef NDN_NAC_ACCESS_MANAGER_HPP 21 | #define NDN_NAC_ACCESS_MANAGER_HPP 22 | 23 | #include "common.hpp" 24 | 25 | #include 26 | 27 | namespace ndn::nac { 28 | 29 | /** 30 | * @brief Access Manager 31 | * 32 | * Access Manager controls decryption policy by publishing granular per-namespace access 33 | * policies in the form of key encryption (KEK, plaintext public) and key decryption (KDK, 34 | * encrypted private key) key pair. 35 | * 36 | * @todo Rolling KEK 37 | */ 38 | class AccessManager 39 | { 40 | public: 41 | class Error : public std::runtime_error 42 | { 43 | public: 44 | using std::runtime_error::runtime_error; 45 | }; 46 | 47 | public: 48 | /** 49 | * @param identity Data owner's namespace identity (will be used to sign KEK and KDK) 50 | * @param dataset Name of dataset that this manager is controlling 51 | * @param keyChain KeyChain 52 | * @param face Face that will be used to publish KEK and KDKs 53 | * 54 | * KEK and KDK naming: 55 | * 56 | * [identity]/NAC/[dataset]/KEK /[key-id] (== KEK, public key) 57 | * 58 | * [identity]/NAC/[dataset]/KDK/[key-id] /ENCRYPTED-BY/[user]/KEY/[key-id] (== KDK, encrypted private key) 59 | * 60 | * \_____________ ______________/ 61 | * \/ 62 | * registered with NFD 63 | * 64 | * AccessManager serves NAC public key for data producers to fetch and encrypted versions of 65 | * private keys (as safe bags) for authorized consumers to fetch. 66 | */ 67 | AccessManager(const Identity& identity, const Name& dataset, 68 | KeyChain& keyChain, Face& face); 69 | 70 | /** 71 | * @brief Authorize a member identified by its certificate @p memberCert to decrypt data 72 | * under the policy 73 | * @return published KDK 74 | */ 75 | Data 76 | addMember(const Certificate& memberCert); 77 | 78 | // void 79 | // addMemberWithKey(const Name& keyName); 80 | 81 | // void 82 | // addMemberWithIdentity(const Name& identityName); 83 | 84 | /** 85 | * @brief Remove member with name @p identity from the group 86 | */ 87 | void 88 | removeMember(const Name& identity); 89 | 90 | public: // accessor interface for published data packets 91 | 92 | /** @return{ number of packets stored in in-memory storage } 93 | */ 94 | size_t 95 | size() const 96 | { 97 | return m_ims.size(); 98 | } 99 | 100 | /** @brief Returns begin iterator of the in-memory storage ordered by 101 | * name with digest 102 | * 103 | * @return{ const_iterator pointing to the beginning of m_cache } 104 | */ 105 | InMemoryStorage::const_iterator 106 | begin() const 107 | { 108 | return m_ims.begin(); 109 | } 110 | 111 | /** @brief Returns end iterator of the in-memory storage ordered by 112 | * name with digest 113 | * 114 | * @return{ const_iterator pointing to the end of m_cache } 115 | */ 116 | InMemoryStorage::const_iterator 117 | end() const 118 | { 119 | return m_ims.end(); 120 | } 121 | 122 | private: 123 | Identity m_identity; 124 | Key m_nacKey; 125 | KeyChain& m_keyChain; 126 | Face& m_face; 127 | 128 | InMemoryStoragePersistent m_ims; // for KEK and KDKs 129 | ScopedRegisteredPrefixHandle m_kekReg; 130 | ScopedRegisteredPrefixHandle m_kdkReg; 131 | }; 132 | 133 | } // namespace ndn::nac 134 | 135 | #endif // NDN_NAC_ACCESS_MANAGER_HPP 136 | -------------------------------------------------------------------------------- /src/common.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include "common.hpp" 21 | 22 | namespace ndn::nac { 23 | 24 | Name 25 | convertKekNameToKdkPrefix(const Name& kekName, const ErrorCallback& onFailure) 26 | { 27 | // * /KEK/ =>> /KDK/ 28 | if (kekName.size() < 2 || kekName.get(-2) != KEK) { 29 | onFailure(ErrorCode::KekInvalidName, "Invalid KEK name [" + kekName.toUri() + "]"); 30 | return {}; 31 | } 32 | 33 | return kekName.getPrefix(-2).append(KDK).append(kekName.get(-1)); 34 | } 35 | 36 | std::tuple 37 | extractKdkInfoFromCkName(const Name& ckDataName, const Name& ckName, const ErrorCallback& onFailure) 38 | { 39 | // | /ENCRYPTED-BY//NAC/KEK/ 40 | 41 | if (ckDataName.size() < ckName.size() + 1 || 42 | ckDataName.getPrefix(ckName.size()) != ckName || 43 | ckDataName.get(ckName.size()) != ENCRYPTED_BY) { 44 | onFailure(ErrorCode::CkInvalidName, "Invalid CK name [" + ckDataName.toUri() + "]"); 45 | return {}; 46 | } 47 | 48 | auto kekName = ckDataName.getSubName(ckName.size() + 1); 49 | return {convertKekNameToKdkPrefix(kekName, onFailure), 50 | kekName.getPrefix(-2), 51 | kekName.getPrefix(-2).append("KEY").append(kekName.get(-1))}; 52 | } 53 | 54 | } // namespace ndn::nac 55 | -------------------------------------------------------------------------------- /src/common.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2023, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #ifndef NDN_NAC_COMMON_HPP 21 | #define NDN_NAC_COMMON_HPP 22 | 23 | #include "detail/config.hpp" 24 | 25 | #ifdef NAC_WITH_TESTS 26 | #define NAC_VIRTUAL_WITH_TESTS virtual 27 | #define NAC_PUBLIC_WITH_TESTS_ELSE_PROTECTED public 28 | #define NAC_PUBLIC_WITH_TESTS_ELSE_PRIVATE public 29 | #define NAC_PROTECTED_WITH_TESTS_ELSE_PRIVATE protected 30 | #else 31 | #define NAC_VIRTUAL_WITH_TESTS 32 | #define NAC_PUBLIC_WITH_TESTS_ELSE_PROTECTED protected 33 | #define NAC_PUBLIC_WITH_TESTS_ELSE_PRIVATE private 34 | #define NAC_PROTECTED_WITH_TESTS_ELSE_PRIVATE private 35 | #endif 36 | 37 | #include 38 | #include 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | #include 54 | 55 | namespace ndn::nac { 56 | 57 | using security::Certificate; 58 | using security::DataValidationFailureCallback; 59 | using security::DataValidationSuccessCallback; 60 | using security::Identity; 61 | using security::Key; 62 | using security::SafeBag; 63 | using security::SigningInfo; 64 | using security::ValidationError; 65 | using security::Validator; 66 | using security::extractKeyNameFromCertName; 67 | using security::transform::PublicKey; 68 | 69 | namespace tlv { 70 | 71 | using namespace ndn::tlv; 72 | 73 | enum { 74 | EncryptedContent = 130, 75 | EncryptedPayload = 132, 76 | InitializationVector = 133, 77 | EncryptedPayloadKey = 134, 78 | }; 79 | 80 | } // namespace tlv 81 | 82 | inline const name::Component ENCRYPTED_BY{"ENCRYPTED-BY"}; 83 | inline const name::Component NAC{"NAC"}; 84 | inline const name::Component KEK{"KEK"}; 85 | inline const name::Component KDK{"KDK"}; 86 | inline const name::Component CK{"CK"}; 87 | 88 | inline constexpr size_t AES_KEY_SIZE = 32; 89 | inline constexpr size_t AES_IV_SIZE = 16; 90 | 91 | inline constexpr time::seconds DEFAULT_KEK_FRESHNESS_PERIOD = 1_h; 92 | inline constexpr time::seconds DEFAULT_KDK_FRESHNESS_PERIOD = 1_h; 93 | inline constexpr time::seconds DEFAULT_CK_FRESHNESS_PERIOD = 1_h; 94 | 95 | inline constexpr time::seconds RETRY_DELAY_AFTER_NACK = 1_s; 96 | inline constexpr time::seconds RETRY_DELAY_KEK_RETRIEVAL = 60_s; 97 | 98 | enum class ErrorCode { 99 | KekRetrievalFailure = 1, 100 | KekRetrievalTimeout = 2, 101 | KekInvalidName = 3, 102 | 103 | KdkRetrievalFailure = 11, 104 | KdkRetrievalTimeout = 12, 105 | KdkInvalidName = 13, 106 | KdkDecryptionFailure = 14, 107 | 108 | CkRetrievalFailure = 21, 109 | CkRetrievalTimeout = 22, 110 | CkInvalidName = 23, 111 | 112 | MissingRequiredKeyLocator = 101, 113 | TpmKeyNotFound = 102, 114 | EncryptionFailure = 103 115 | }; 116 | 117 | using ErrorCallback = std::function; 118 | 119 | class Error : public std::runtime_error 120 | { 121 | public: 122 | using std::runtime_error::runtime_error; 123 | }; 124 | 125 | /** 126 | * @brief Convert KEK name to KDK prefix: 127 | * 128 | * `/NAC/KEK/` =>> `/NAC/KDK/` 129 | */ 130 | Name 131 | convertKekNameToKdkPrefix(const Name& kekName, const ErrorCallback& onFailure); 132 | 133 | /** 134 | * @brief Extract KDK information from name of CK data packet name 135 | * 136 | * @return tuple of (KDK prefix, KDK identity, and KDK key id). The last two identify 137 | * KDK private/key pair in KeyChain 138 | */ 139 | std::tuple 140 | extractKdkInfoFromCkName(const Name& ckDataName, const Name& ckName, const ErrorCallback& onFailure); 141 | 142 | } // namespace ndn::nac 143 | 144 | #endif // NDN_NAC_COMMON_HPP 145 | -------------------------------------------------------------------------------- /src/decryptor.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include "decryptor.hpp" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | namespace ndn::nac { 31 | 32 | NDN_LOG_INIT(nac.Decryptor); 33 | 34 | constexpr size_t N_RETRIES = 3; 35 | 36 | Decryptor::Decryptor(const Key& credentialsKey, Validator& validator, KeyChain& keyChain, Face& face) 37 | : m_credentialsKey(credentialsKey) 38 | // , m_validator(validator) 39 | , m_face(face) 40 | , m_keyChain(keyChain) 41 | , m_internalKeyChain("pib-memory:", "tpm-memory:") 42 | { 43 | } 44 | 45 | Decryptor::~Decryptor() 46 | { 47 | for (auto& i : m_cks) { 48 | if (i.second.pendingInterest) { 49 | i.second.pendingInterest->cancel(); 50 | for (const auto& p : i.second.pendingDecrypts) { 51 | p.onFailure(ErrorCode::CkRetrievalFailure, 52 | "Cancel pending decrypt as ContentKey is being destroyed"); 53 | } 54 | } 55 | } 56 | } 57 | 58 | void 59 | Decryptor::decrypt(const Block& encryptedContent, 60 | const DecryptSuccessCallback& onSuccess, 61 | const ErrorCallback& onFailure) 62 | { 63 | EncryptedContent ec(encryptedContent); 64 | if (!ec.hasKeyLocator()) { 65 | NDN_LOG_INFO("Missing required KeyLocator in the supplied EncryptedContent block"); 66 | return onFailure(ErrorCode::MissingRequiredKeyLocator, 67 | "Missing required KeyLocator in the supplied EncryptedContent block"); 68 | } 69 | 70 | if (!ec.hasIv()) { 71 | NDN_LOG_INFO("Missing required InitialVector in the supplied EncryptedContent block"); 72 | return onFailure(ErrorCode::MissingRequiredKeyLocator, 73 | "Missing required InitialVector in the supplied EncryptedContent block"); 74 | } 75 | 76 | auto [ck, isNew] = m_cks.emplace(ec.getKeyLocator(), ContentKey{}); 77 | 78 | if (ck->second.isRetrieved) { 79 | doDecrypt(ec, ck->second.bits, onSuccess, onFailure); 80 | } 81 | else { 82 | NDN_LOG_DEBUG("CK " << ec.getKeyLocator() << " not yet available, adding decrypt to the pending queue"); 83 | ck->second.pendingDecrypts.push_back({ec, onSuccess, onFailure}); 84 | } 85 | 86 | if (isNew) { 87 | fetchCk(ck, onFailure, N_RETRIES); 88 | } 89 | } 90 | 91 | void 92 | Decryptor::fetchCk(ContentKeys::iterator ck, const ErrorCallback& onFailure, size_t nTriesLeft) 93 | { 94 | // full name of CK is 95 | 96 | // /CK/ /ENCRYPTED-BY //KEK/ 97 | // \ / \ / 98 | // ----------- ------------- ----------- ----------- 99 | // \/ \/ 100 | // from the encrypted data unknown (name in retrieved CK is used to determine KDK) 101 | 102 | const Name& ckName = ck->first; 103 | NDN_LOG_DEBUG("Fetching CK " << ckName); 104 | 105 | ck->second.pendingInterest = m_face.expressInterest(Interest(ckName) 106 | .setMustBeFresh(false) // ? 107 | .setCanBePrefix(true), 108 | [=] (const Interest& ckInterest, const Data& ckData) { 109 | ck->second.pendingInterest = std::nullopt; 110 | // TODO: verify that the key is legit 111 | auto [kdkPrefix, kdkIdentity, kdkKeyName] = 112 | extractKdkInfoFromCkName(ckData.getName(), ckInterest.getName(), onFailure); 113 | if (kdkPrefix.empty()) { 114 | return; // error has been already reported 115 | } 116 | 117 | // check if KDK already exists (there is a corresponding 118 | auto kdkIdentityIt = m_internalKeyChain.getPib().getIdentities().find(kdkIdentity); 119 | if (kdkIdentityIt != m_internalKeyChain.getPib().getIdentities().end()) { 120 | auto kdkKeyIt = (*kdkIdentityIt).getKeys().find(kdkKeyName); 121 | if (kdkKeyIt != (*kdkIdentityIt).getKeys().end()) { 122 | // KDK was already fetched and imported 123 | NDN_LOG_DEBUG("KDK " << kdkKeyName << " already exists, directly using it to decrypt CK"); 124 | return decryptCkAndProcessPendingDecrypts(ck, ckData, kdkKeyName, onFailure); 125 | } 126 | } 127 | 128 | fetchKdk(ck, kdkPrefix, ckData, onFailure, N_RETRIES); 129 | }, 130 | [=] (const Interest& i, const lp::Nack& nack) { 131 | ck->second.pendingInterest = std::nullopt; 132 | onFailure(ErrorCode::CkRetrievalFailure, 133 | "Retrieval of CK [" + i.getName().toUri() + "] failed. " 134 | "Got NACK (" + boost::lexical_cast(nack.getReason()) + ")"); 135 | }, 136 | [=] (const Interest& i) { 137 | ck->second.pendingInterest = std::nullopt; 138 | if (nTriesLeft > 1) { 139 | fetchCk(ck, onFailure, nTriesLeft - 1); 140 | } 141 | else { 142 | onFailure(ErrorCode::CkRetrievalTimeout, 143 | "Retrieval of CK [" + i.getName().toUri() + "] timed out"); 144 | } 145 | }); 146 | } 147 | 148 | void 149 | Decryptor::fetchKdk(ContentKeys::iterator ck, const Name& kdkPrefix, const Data& ckData, 150 | const ErrorCallback& onFailure, size_t nTriesLeft) 151 | { 152 | // /KDK/ /ENCRYPTED-BY //KEY/ 153 | // \ / \ / 154 | // ----------- ------------- --------------- --------------- 155 | // \/ \/ 156 | // from the CK data from configuration 157 | 158 | Name kdkName = kdkPrefix; 159 | kdkName 160 | .append(ENCRYPTED_BY) 161 | .append(m_credentialsKey.getName()); 162 | 163 | NDN_LOG_DEBUG("Fetching KDK " << kdkName); 164 | 165 | ck->second.pendingInterest = m_face.expressInterest(Interest(kdkName).setMustBeFresh(true), 166 | [=] (const Interest&, const Data& kdkData) { 167 | ck->second.pendingInterest = std::nullopt; 168 | // TODO: verify that the key is legit 169 | 170 | bool isOk = decryptAndImportKdk(kdkData, onFailure); 171 | if (!isOk) 172 | return; 173 | decryptCkAndProcessPendingDecrypts(ck, ckData, 174 | kdkPrefix.getPrefix(-2).append("KEY").append(kdkPrefix.get(-1)), // a bit hacky 175 | onFailure); 176 | }, 177 | [=] (const Interest& i, const lp::Nack& nack) { 178 | ck->second.pendingInterest = std::nullopt; 179 | onFailure(ErrorCode::KdkRetrievalFailure, 180 | "Retrieval of KDK [" + i.getName().toUri() + "] failed. " 181 | "Got NACK (" + boost::lexical_cast(nack.getReason()) + ")"); 182 | }, 183 | [=] (const Interest& i) { 184 | ck->second.pendingInterest = std::nullopt; 185 | if (nTriesLeft > 1) { 186 | fetchKdk(ck, kdkPrefix, ckData, onFailure, nTriesLeft - 1); 187 | } 188 | else { 189 | onFailure(ErrorCode::KdkRetrievalTimeout, 190 | "Retrieval of KDK [" + i.getName().toUri() + "] timed out"); 191 | } 192 | }); 193 | } 194 | 195 | bool 196 | Decryptor::decryptAndImportKdk(const Data& kdkData, const ErrorCallback& onFailure) 197 | { 198 | try { 199 | NDN_LOG_DEBUG("Decrypting and importing KDK " << kdkData.getName()); 200 | EncryptedContent content(kdkData.getContent().blockFromValue()); 201 | 202 | SafeBag safeBag(content.getPayload().blockFromValue()); 203 | auto secret = m_keyChain.getTpm().decrypt(content.getPayloadKey().value_bytes(), 204 | m_credentialsKey.getName()); 205 | if (secret == nullptr) { 206 | onFailure(ErrorCode::TpmKeyNotFound, 207 | "Could not decrypt secret, " + m_credentialsKey.getName().toUri() + " not found in TPM"); 208 | return false; 209 | } 210 | 211 | m_internalKeyChain.importSafeBag(safeBag, reinterpret_cast(secret->data()), secret->size()); 212 | return true; 213 | } 214 | catch (const std::runtime_error& e) { 215 | // can be tlv::Error, pib::Error, tpm::Error, and bunch of other runtime-derived errors 216 | onFailure(ErrorCode::KdkDecryptionFailure, 217 | "Failed to decrypt KDK [" + kdkData.getName().toUri() + "]: " + e.what()); 218 | return false; 219 | } 220 | } 221 | 222 | void 223 | Decryptor::decryptCkAndProcessPendingDecrypts(ContentKeys::iterator ck, const Data& ckData, const Name& kdkKeyName, 224 | const ErrorCallback& onFailure) 225 | { 226 | NDN_LOG_DEBUG("Decrypting CK data " << ckData.getName()); 227 | 228 | EncryptedContent content(ckData.getContent().blockFromValue()); 229 | 230 | auto ckBits = m_internalKeyChain.getTpm().decrypt(content.getPayload().value_bytes(), kdkKeyName); 231 | if (ckBits == nullptr) { 232 | onFailure(ErrorCode::TpmKeyNotFound, "Could not decrypt secret, " + kdkKeyName.toUri() + " not found in TPM"); 233 | return; 234 | } 235 | 236 | ck->second.bits = *ckBits; 237 | ck->second.isRetrieved = true; 238 | 239 | for (const auto& item : ck->second.pendingDecrypts) { 240 | doDecrypt(item.encryptedContent, ck->second.bits, item.onSuccess, item.onFailure); 241 | } 242 | ck->second.pendingDecrypts.clear(); 243 | } 244 | 245 | void 246 | Decryptor::doDecrypt(const EncryptedContent& content, const Buffer& ckBits, 247 | const DecryptSuccessCallback& onSuccess, 248 | const ErrorCallback& onFailure) 249 | { 250 | if (!content.hasIv()) { 251 | NDN_THROW(Error("Expecting Initialization Vector in the encrypted content, but it is not present")); 252 | } 253 | 254 | OBufferStream os; 255 | security::transform::bufferSource(content.getPayload().value_bytes()) 256 | >> security::transform::blockCipher(BlockCipherAlgorithm::AES_CBC, 257 | CipherOperator::DECRYPT, 258 | ckBits, content.getIv().value_bytes()) 259 | >> security::transform::streamSink(os); 260 | 261 | onSuccess(os.buf()); 262 | } 263 | 264 | } // namespace ndn::nac 265 | -------------------------------------------------------------------------------- /src/decryptor.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #ifndef NDN_NAC_DECRYPTOR_HPP 21 | #define NDN_NAC_DECRYPTOR_HPP 22 | 23 | #include "common.hpp" 24 | #include "encrypted-content.hpp" 25 | 26 | #include 27 | #include 28 | 29 | namespace ndn::nac { 30 | 31 | /** 32 | * @brief NAC Decryptor 33 | * 34 | * Encryptor decrypts (asynchronous operation, contingent on successful retrieval of CK data, 35 | * KDK, and decryption of both) the supplied ``EncryptedContent`` element. 36 | */ 37 | class Decryptor 38 | { 39 | public: 40 | using DecryptSuccessCallback = std::function; 41 | 42 | /** 43 | * @brief Constructor 44 | * @param credentialsKey Credentials key to be used to retrieve and decrypt KDK 45 | * @param validator Validation policy to ensure validity of KDK and CK 46 | * @param keyChain KeyChain 47 | * @param face Face that will be used to fetch CK and KDK 48 | */ 49 | Decryptor(const Key& credentialsKey, Validator& validator, KeyChain& keyChain, Face& face); 50 | 51 | ~Decryptor(); 52 | 53 | /** 54 | * @brief Asynchronously decrypt @p encryptedContent 55 | */ 56 | void 57 | decrypt(const Block& encryptedContent, 58 | const DecryptSuccessCallback& onSuccess, const ErrorCallback& onFailure); 59 | 60 | private: 61 | struct ContentKey 62 | { 63 | bool isRetrieved = false; 64 | Buffer bits; 65 | std::optional pendingInterest; 66 | 67 | struct PendingDecrypt 68 | { 69 | EncryptedContent encryptedContent; 70 | DecryptSuccessCallback onSuccess; 71 | ErrorCallback onFailure; 72 | }; 73 | std::list pendingDecrypts; 74 | }; 75 | 76 | using ContentKeys = std::map; 77 | 78 | void 79 | fetchCk(ContentKeys::iterator ck, const ErrorCallback& onFailure, size_t nTriesLeft); 80 | 81 | void 82 | fetchKdk(ContentKeys::iterator ck, const Name& kdkPrefix, const Data& ckData, 83 | const ErrorCallback& onFailure, size_t nTriesLeft); 84 | 85 | bool 86 | decryptAndImportKdk(const Data& kdkData, const ErrorCallback& onFailure); 87 | 88 | void 89 | decryptCkAndProcessPendingDecrypts(ContentKeys::iterator ck, const Data& ckData, 90 | const Name& kdkKeyName/* local keyChain name for KDK key*/, 91 | const ErrorCallback& onFailure); 92 | 93 | /** 94 | * @brief Synchronously decrypt 95 | */ 96 | static void 97 | doDecrypt(const EncryptedContent& encryptedContent, const Buffer& ckBits, 98 | const DecryptSuccessCallback& onSuccess, 99 | const ErrorCallback& onFailure); 100 | 101 | private: 102 | Key m_credentialsKey; 103 | // Validator& m_validator; 104 | Face& m_face; 105 | KeyChain& m_keyChain; // external keychain with access credentials 106 | KeyChain m_internalKeyChain; // internal in-memory keychain for temporarily storing KDKs 107 | 108 | // a set of Content Keys 109 | // TODO: add some expiration, so they are not stored forever 110 | ContentKeys m_cks; 111 | }; 112 | 113 | } // namespace ndn::nac 114 | 115 | #endif // NDN_NAC_DECRYPTOR_HPP 116 | -------------------------------------------------------------------------------- /src/encrypted-content.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include "encrypted-content.hpp" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace ndn::nac { 27 | 28 | BOOST_CONCEPT_ASSERT((WireEncodable)); 29 | BOOST_CONCEPT_ASSERT((WireDecodable)); 30 | static_assert(std::is_base_of_v, 31 | "EncryptedContent::Error must inherit from tlv::Error"); 32 | 33 | EncryptedContent::EncryptedContent(const Block& block) 34 | { 35 | wireDecode(block); 36 | } 37 | 38 | EncryptedContent& 39 | EncryptedContent::setPayload(Block payload) 40 | { 41 | m_wire.reset(); 42 | if (payload.type() != tlv::EncryptedPayload) { 43 | m_payload = Block(tlv::EncryptedPayload, payload); 44 | } 45 | else { 46 | m_payload = std::move(payload); 47 | } 48 | return *this; 49 | } 50 | 51 | EncryptedContent& 52 | EncryptedContent::setPayload(ConstBufferPtr payload) 53 | { 54 | m_wire.reset(); 55 | m_payload = Block(tlv::EncryptedPayload, std::move(payload)); 56 | return *this; 57 | } 58 | 59 | EncryptedContent& 60 | EncryptedContent::setIv(Block iv) 61 | { 62 | m_wire.reset(); 63 | if (iv.type() != tlv::InitializationVector) { 64 | m_iv = Block(tlv::InitializationVector, iv); 65 | } 66 | else { 67 | m_iv = std::move(iv); 68 | } 69 | return *this; 70 | } 71 | 72 | EncryptedContent& 73 | EncryptedContent::setIv(ConstBufferPtr iv) 74 | { 75 | m_wire.reset(); 76 | m_iv = Block(tlv::InitializationVector, std::move(iv)); 77 | return *this; 78 | } 79 | 80 | EncryptedContent& 81 | EncryptedContent::unsetIv() 82 | { 83 | m_wire.reset(); 84 | m_iv = {}; 85 | return *this; 86 | } 87 | 88 | EncryptedContent& 89 | EncryptedContent::setPayloadKey(Block key) 90 | { 91 | m_wire.reset(); 92 | if (key.type() != tlv::EncryptedPayloadKey) { 93 | m_payloadKey = Block(tlv::EncryptedPayloadKey, key); 94 | } 95 | else { 96 | m_payloadKey = std::move(key); 97 | } 98 | return *this; 99 | } 100 | 101 | EncryptedContent& 102 | EncryptedContent::setPayloadKey(ConstBufferPtr key) 103 | { 104 | m_wire.reset(); 105 | m_payloadKey = Block(tlv::EncryptedPayloadKey, std::move(key)); 106 | return *this; 107 | } 108 | 109 | EncryptedContent& 110 | EncryptedContent::unsetPayloadKey() 111 | { 112 | m_wire.reset(); 113 | m_payloadKey = {}; 114 | return *this; 115 | } 116 | 117 | EncryptedContent& 118 | EncryptedContent::setKeyLocator(Name keyLocator) 119 | { 120 | m_wire.reset(); 121 | m_keyLocator = std::move(keyLocator); 122 | return *this; 123 | } 124 | 125 | EncryptedContent& 126 | EncryptedContent::unsetKeyLocator() 127 | { 128 | m_wire.reset(); 129 | m_keyLocator = {}; 130 | return *this; 131 | } 132 | 133 | template 134 | size_t 135 | EncryptedContent::wireEncode(EncodingImpl& encoder) const 136 | { 137 | size_t totalLength = 0; 138 | 139 | if (hasKeyLocator()) { 140 | totalLength += m_keyLocator.wireEncode(encoder); 141 | } 142 | 143 | if (hasPayloadKey()) { 144 | totalLength += prependBlock(encoder, m_payloadKey); 145 | } 146 | 147 | if (hasIv()) { 148 | totalLength += prependBlock(encoder, m_iv); 149 | } 150 | 151 | if (m_payload.isValid()) { 152 | totalLength += prependBlock(encoder, m_payload); 153 | } 154 | else { 155 | NDN_THROW(Error("Required EncryptedPayload is not set on EncryptedContent")); 156 | } 157 | 158 | totalLength += encoder.prependVarNumber(totalLength); 159 | totalLength += encoder.prependVarNumber(tlv::EncryptedContent); 160 | return totalLength; 161 | } 162 | 163 | const Block& 164 | EncryptedContent::wireEncode() const 165 | { 166 | if (m_wire.hasWire()) 167 | return m_wire; 168 | 169 | EncodingEstimator estimator; 170 | size_t estimatedSize = wireEncode(estimator); 171 | 172 | EncodingBuffer buffer(estimatedSize, 0); 173 | wireEncode(buffer); 174 | 175 | m_wire = buffer.block(); 176 | return m_wire; 177 | } 178 | 179 | void 180 | EncryptedContent::wireDecode(const Block& wire) 181 | { 182 | if (!wire.hasWire()) { 183 | NDN_THROW(Error("The supplied block does not contain wire format")); 184 | } 185 | if (wire.type() != tlv::EncryptedContent) { 186 | NDN_THROW(Error("EncryptedContent", wire.type())); 187 | } 188 | 189 | *this = {}; 190 | m_wire = wire; 191 | m_wire.parse(); 192 | 193 | auto block = m_wire.find(tlv::EncryptedPayload); 194 | if (block != m_wire.elements_end()) { 195 | m_payload = *block; 196 | } 197 | else { 198 | NDN_THROW(Error("Required EncryptedPayload not found in EncryptedContent")); 199 | } 200 | 201 | block = m_wire.find(tlv::InitializationVector); 202 | if (block != m_wire.elements_end()) { 203 | m_iv = *block; 204 | } 205 | 206 | block = m_wire.find(tlv::EncryptedPayloadKey); 207 | if (block != m_wire.elements_end()) { 208 | m_payloadKey = *block; 209 | } 210 | 211 | block = m_wire.find(tlv::Name); 212 | if (block != m_wire.elements_end()) { 213 | m_keyLocator.wireDecode(*block); 214 | } 215 | } 216 | 217 | } // namespace ndn::nac 218 | -------------------------------------------------------------------------------- /src/encrypted-content.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #ifndef NDN_NAC_ENCRYPTED_CONTENT_HPP 21 | #define NDN_NAC_ENCRYPTED_CONTENT_HPP 22 | 23 | #include "common.hpp" 24 | 25 | #include 26 | 27 | namespace ndn::nac { 28 | 29 | /** 30 | * @brief Encrypted content 31 | * 32 | * @verbatim 33 | * EncryptedContent ::= ENCRYPTED-CONTENT-TYPE TLV-LENGTH 34 | * InitializationVector 35 | * EncryptedPayload 36 | * EncryptedPayloadKey 37 | * Name 38 | * 39 | * InitializationVector ::= INITIALIZATION-VECTOR-TYPE TLV-LENGTH(=N) BYTE{N} 40 | * EncryptedPayload ::= ENCRYPTED-PAYLOAD-TYPE TLV-LENGTH(=N) BYTE{N} 41 | * EncryptedPayloadKey ::= ENCRYPTED-PAYLOAD-KEY-TYPE TLV-LENGTH(=N) BYTE{N} 42 | * @endverbatim 43 | */ 44 | class EncryptedContent 45 | { 46 | public: 47 | class Error : public ndn::tlv::Error 48 | { 49 | public: 50 | using ndn::tlv::Error::Error; 51 | }; 52 | 53 | public: 54 | EncryptedContent() = default; 55 | 56 | explicit 57 | EncryptedContent(const Block& block); 58 | 59 | const Block& 60 | getPayload() const 61 | { 62 | return m_payload; 63 | } 64 | 65 | EncryptedContent& 66 | setPayload(Block payload); 67 | 68 | EncryptedContent& 69 | setPayload(ConstBufferPtr payload); 70 | 71 | bool 72 | hasIv() const noexcept 73 | { 74 | return m_iv.isValid(); 75 | } 76 | 77 | const Block& 78 | getIv() const 79 | { 80 | return m_iv; 81 | } 82 | 83 | EncryptedContent& 84 | unsetIv(); 85 | 86 | EncryptedContent& 87 | setIv(Block iv); 88 | 89 | EncryptedContent& 90 | setIv(ConstBufferPtr iv); 91 | 92 | bool 93 | hasPayloadKey() const noexcept 94 | { 95 | return m_payloadKey.isValid(); 96 | } 97 | 98 | const Block& 99 | getPayloadKey() const 100 | { 101 | return m_payloadKey; 102 | } 103 | 104 | EncryptedContent& 105 | setPayloadKey(Block key); 106 | 107 | EncryptedContent& 108 | setPayloadKey(ConstBufferPtr key); 109 | 110 | EncryptedContent& 111 | unsetPayloadKey(); 112 | 113 | bool 114 | hasKeyLocator() const 115 | { 116 | return !m_keyLocator.empty(); 117 | } 118 | 119 | const Name& 120 | getKeyLocator() const 121 | { 122 | return m_keyLocator; 123 | } 124 | 125 | EncryptedContent& 126 | setKeyLocator(Name keyLocator); 127 | 128 | EncryptedContent& 129 | unsetKeyLocator(); 130 | 131 | template 132 | size_t 133 | wireEncode(EncodingImpl& block) const; 134 | 135 | const Block& 136 | wireEncode() const; 137 | 138 | void 139 | wireDecode(const Block& wire); 140 | 141 | private: 142 | Block m_iv; 143 | Block m_payload; 144 | Block m_payloadKey; ///< for public key encryption, public key encodes a random key that is used 145 | ///< for symmetric encryption of the content 146 | Name m_keyLocator; 147 | 148 | mutable Block m_wire; 149 | }; 150 | 151 | } // namespace ndn::nac 152 | 153 | #endif // NDN_NAC_ENCRYPTED_CONTENT_HPP 154 | -------------------------------------------------------------------------------- /src/encryptor.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include "encryptor.hpp" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | namespace ndn::nac { 31 | 32 | NDN_LOG_INIT(nac.Encryptor); 33 | 34 | constexpr size_t N_RETRIES = 3; 35 | 36 | Encryptor::Encryptor(const Name& accessPrefix, 37 | const Name& ckPrefix, SigningInfo ckDataSigningInfo, 38 | const ErrorCallback& onFailure, 39 | Validator&, KeyChain& keyChain, Face& face) 40 | : m_accessPrefix(accessPrefix) 41 | , m_ckPrefix(ckPrefix) 42 | , m_ckBits(AES_KEY_SIZE) 43 | , m_ckDataSigningInfo(std::move(ckDataSigningInfo)) 44 | , m_isKekRetrievalInProgress(false) 45 | , m_onFailure(onFailure) 46 | , m_keyChain(keyChain) 47 | , m_face(face) 48 | , m_scheduler(face.getIoContext()) 49 | { 50 | regenerateCk(); 51 | 52 | auto serveFromIms = [this] (const Name&, const Interest& interest) { 53 | auto data = m_ims.find(interest); 54 | if (data != nullptr) { 55 | NDN_LOG_DEBUG("Serving " << data->getName() << " from InMemoryStorage"); 56 | m_face.put(*data); 57 | } 58 | else { 59 | NDN_LOG_DEBUG("Didn't find CK data for " << interest.getName()); 60 | // send NACK? 61 | } 62 | }; 63 | 64 | auto handleError = [] (const Name& prefix, const std::string& msg) { 65 | NDN_LOG_ERROR("Failed to register prefix " << prefix << ": " << msg); 66 | }; 67 | 68 | m_ckReg = m_face.setInterestFilter(Name(ckPrefix).append(CK), serveFromIms, handleError); 69 | } 70 | 71 | Encryptor::~Encryptor() 72 | { 73 | m_kekPendingInterest.cancel(); 74 | } 75 | 76 | void 77 | Encryptor::retryFetchingKek() 78 | { 79 | if (m_isKekRetrievalInProgress) { 80 | return; 81 | } 82 | 83 | NDN_LOG_DEBUG("Retrying fetching of KEK"); 84 | m_isKekRetrievalInProgress = true; 85 | fetchKekAndPublishCkData([&] { 86 | NDN_LOG_DEBUG("KEK retrieved and published"); 87 | m_isKekRetrievalInProgress = false; 88 | }, 89 | [=] (const ErrorCode& code, const std::string& msg) { 90 | NDN_LOG_ERROR("Failed to retrieved KEK: " + msg); 91 | m_isKekRetrievalInProgress = false; 92 | m_onFailure(code, msg); 93 | }, 94 | N_RETRIES); 95 | } 96 | 97 | void 98 | Encryptor::regenerateCk() 99 | { 100 | m_ckName = m_ckPrefix; 101 | m_ckName 102 | .append(CK) 103 | .appendVersion(); // version = ID of CK 104 | NDN_LOG_DEBUG("Generating new CK: " << m_ckName); 105 | random::generateSecureBytes(m_ckBits); 106 | 107 | // one implication: if CK updated before KEK fetched, KDK for the old CK will not be published 108 | if (!m_kek) { 109 | retryFetchingKek(); 110 | } 111 | else { 112 | makeAndPublishCkData(m_onFailure); 113 | } 114 | } 115 | 116 | EncryptedContent 117 | Encryptor::encrypt(span data) 118 | { 119 | // Generate IV 120 | auto iv = std::make_shared(AES_IV_SIZE); 121 | random::generateSecureBytes(*iv); 122 | 123 | OBufferStream os; 124 | security::transform::bufferSource(data) 125 | >> security::transform::blockCipher(BlockCipherAlgorithm::AES_CBC, 126 | CipherOperator::ENCRYPT, 127 | m_ckBits, *iv) 128 | >> security::transform::streamSink(os); 129 | 130 | EncryptedContent content; 131 | content.setIv(iv); 132 | content.setPayload(os.buf()); 133 | content.setKeyLocator(m_ckName); 134 | 135 | return content; 136 | } 137 | 138 | void 139 | Encryptor::fetchKekAndPublishCkData(const std::function& onReady, 140 | const ErrorCallback& onFailure, 141 | size_t nTriesLeft) 142 | { 143 | // interest for /KEK to retrieve /KEK/ KekData 144 | 145 | NDN_LOG_DEBUG("Fetching KEK " << Name(m_accessPrefix).append(KEK)); 146 | 147 | auto kekInterest = Interest(Name(m_accessPrefix).append(KEK)) 148 | .setCanBePrefix(true) 149 | .setMustBeFresh(true); 150 | m_kekPendingInterest = m_face.expressInterest(kekInterest, 151 | [=] (const Interest&, const Data& kek) { 152 | // @todo verify if the key is legit 153 | m_kek = kek; 154 | if (makeAndPublishCkData(onFailure)) { 155 | onReady(); 156 | } 157 | // otherwise, failure has been already declared 158 | }, 159 | [=] (const Interest& i, const lp::Nack& nack) { 160 | if (nTriesLeft > 1) { 161 | m_scheduler.schedule(RETRY_DELAY_AFTER_NACK, [=] { 162 | fetchKekAndPublishCkData(onReady, onFailure, nTriesLeft - 1); 163 | }); 164 | } 165 | else { 166 | onFailure(ErrorCode::KekRetrievalFailure, "Retrieval of KEK [" + i.getName().toUri() + 167 | "] failed. Got NACK with reason " + boost::lexical_cast(nack.getReason())); 168 | NDN_LOG_DEBUG("Scheduling retry from NACK"); 169 | m_scheduler.schedule(RETRY_DELAY_KEK_RETRIEVAL, [this] { retryFetchingKek(); }); 170 | } 171 | }, 172 | [=] (const Interest& i) { 173 | if (nTriesLeft > 1) { 174 | fetchKekAndPublishCkData(onReady, onFailure, nTriesLeft - 1); 175 | } 176 | else { 177 | onFailure(ErrorCode::KekRetrievalTimeout, 178 | "Retrieval of KEK [" + i.getName().toUri() + "] timed out"); 179 | NDN_LOG_DEBUG("Scheduling retry after all timeouts"); 180 | m_scheduler.schedule(RETRY_DELAY_KEK_RETRIEVAL, [this] { retryFetchingKek(); }); 181 | } 182 | }); 183 | } 184 | 185 | bool 186 | Encryptor::makeAndPublishCkData(const ErrorCallback& onFailure) 187 | { 188 | try { 189 | PublicKey kek; 190 | kek.loadPkcs8(m_kek->getContent().value_bytes()); 191 | 192 | EncryptedContent content; 193 | content.setPayload(kek.encrypt(m_ckBits)); 194 | 195 | auto ckData = std::make_shared(Name(m_ckName).append(ENCRYPTED_BY).append(m_kek->getName())); 196 | ckData->setContent(content.wireEncode()); 197 | // FreshnessPeriod can serve as a soft access control for revoking access 198 | ckData->setFreshnessPeriod(DEFAULT_CK_FRESHNESS_PERIOD); 199 | m_keyChain.sign(*ckData, m_ckDataSigningInfo); 200 | m_ims.insert(*ckData); 201 | 202 | NDN_LOG_DEBUG("Publishing CK data: " << ckData->getName()); 203 | return true; 204 | } 205 | catch (const std::runtime_error&) { 206 | onFailure(ErrorCode::EncryptionFailure, 207 | "Failed to encrypt generated CK with KEK " + m_kek->getName().toUri()); 208 | return false; 209 | } 210 | } 211 | 212 | } // namespace ndn::nac 213 | -------------------------------------------------------------------------------- /src/encryptor.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #ifndef NDN_NAC_ENCRYPTOR_HPP 21 | #define NDN_NAC_ENCRYPTOR_HPP 22 | 23 | #include "common.hpp" 24 | #include "encrypted-content.hpp" 25 | 26 | namespace ndn::nac { 27 | 28 | /** 29 | * @brief NAC Encryptor 30 | * 31 | * Encryptor encrypts the requested content and returns an EncryptedContent element. 32 | */ 33 | class Encryptor 34 | { 35 | public: 36 | /** 37 | * @param accessPrefix NAC prefix to fetch KEK (e.g., /access/prefix/NAC/data/subset) 38 | * @param ckPrefix Prefix under which Content Keys will be generated 39 | * (each will have unique version appended) 40 | * @param ckDataSigningInfo SigningInfo parameters to sign CK Data 41 | * @param onFailure Callback to notify application of a failure to create CK data 42 | * (failed to fetch KEK, failed to encrypt with KEK, etc.). 43 | * Note that Encryptor will continue trying to retrieve KEK until success 44 | * (each attempt separated by `RETRY_DELAY_KEK_RETRIEVAL`) and @p onFailure 45 | * may be called multiple times. 46 | * @param validator Validation policy to ensure correctness of KEK 47 | * @param keyChain KeyChain 48 | * @param face Face that will be used to fetch KEK and publish CK data 49 | */ 50 | Encryptor(const Name& accessPrefix, 51 | const Name& ckPrefix, SigningInfo ckDataSigningInfo, 52 | const ErrorCallback& onFailure, 53 | Validator& validator, KeyChain& keyChain, Face& face); 54 | 55 | ~Encryptor(); 56 | 57 | /** 58 | * @brief Synchronously encrypt supplied data 59 | * 60 | * If KEK has not been fetched already, this method will trigger async fetching of it. 61 | * After KEK successfully fetched, CK data will be automatically published. 62 | * 63 | * @todo For now, CK is being published in InMemoryStorage and can be fetched only while 64 | * Encryptor instance is alive. 65 | * 66 | * The actual encryption is done synchronously, but the exact KDK name is not known 67 | * until KEK is fetched. 68 | * 69 | * Note that if the KDK name is already known, this method will call onReady right away. 70 | * 71 | * @return Encrypted content 72 | */ 73 | EncryptedContent 74 | encrypt(span data); 75 | 76 | /** 77 | * @brief Create a new content key and publish the corresponding CK data 78 | * 79 | * @todo Ensure that CK data packet for the old CK is published, when CK updated 80 | * before KEK fetched 81 | */ 82 | void 83 | regenerateCk(); 84 | 85 | public: // accessor interface for published data packets 86 | /** 87 | * @return number of packets stored in in-memory storage 88 | */ 89 | size_t 90 | size() const 91 | { 92 | return m_ims.size(); 93 | } 94 | 95 | /** 96 | * @brief Returns begin iterator of the in-memory storage ordered by name with digest 97 | * 98 | * @return const_iterator pointing to the beginning of m_cache 99 | */ 100 | InMemoryStorage::const_iterator 101 | begin() const 102 | { 103 | return m_ims.begin(); 104 | } 105 | 106 | /** 107 | * @brief Returns end iterator of the in-memory storage ordered by name with digest 108 | * 109 | * @return const_iterator pointing to the end of m_cache 110 | */ 111 | InMemoryStorage::const_iterator 112 | end() const 113 | { 114 | return m_ims.end(); 115 | } 116 | 117 | private: 118 | void 119 | retryFetchingKek(); 120 | 121 | void 122 | fetchKekAndPublishCkData(const std::function& onReady, 123 | const ErrorCallback& onFailure, 124 | size_t nTriesLeft); 125 | 126 | bool 127 | makeAndPublishCkData(const ErrorCallback& onFailure); 128 | 129 | NAC_PUBLIC_WITH_TESTS_ELSE_PRIVATE: 130 | Name m_accessPrefix; 131 | Name m_ckPrefix; 132 | Name m_ckName; 133 | Buffer m_ckBits; 134 | SigningInfo m_ckDataSigningInfo; 135 | 136 | bool m_isKekRetrievalInProgress; 137 | std::optional m_kek; 138 | ErrorCallback m_onFailure; 139 | 140 | InMemoryStoragePersistent m_ims; // for encrypted CKs 141 | ScopedRegisteredPrefixHandle m_ckReg; 142 | PendingInterestHandle m_kekPendingInterest; 143 | 144 | KeyChain& m_keyChain; 145 | Face& m_face; 146 | Scheduler m_scheduler; 147 | }; 148 | 149 | } // namespace ndn::nac 150 | 151 | #endif // NDN_NAC_ENCRYPTOR_HPP 152 | -------------------------------------------------------------------------------- /src/version.hpp.in: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #ifndef NDN_NAC_VERSION_HPP 21 | #define NDN_NAC_VERSION_HPP 22 | 23 | // ndn-nac version follows Semantic Versioning 2.0.0 24 | // https://semver.org/spec/v2.0.0.html 25 | 26 | /** 27 | * \brief ndn-nac version represented as an integer. 28 | * 29 | * Equivalent to: #NDN_NAC_VERSION_MAJOR*1000000 + #NDN_NAC_VERSION_MINOR*1000 + #NDN_NAC_VERSION_PATCH 30 | */ 31 | #define NDN_NAC_VERSION @VERSION@ 32 | 33 | /// The major version of ndn-nac 34 | #define NDN_NAC_VERSION_MAJOR @VERSION_MAJOR@ 35 | /// The minor version of ndn-nac 36 | #define NDN_NAC_VERSION_MINOR @VERSION_MINOR@ 37 | /// The patch version of ndn-nac 38 | #define NDN_NAC_VERSION_PATCH @VERSION_PATCH@ 39 | 40 | /** 41 | * \brief ndn-nac version represented as a string. 42 | * 43 | * Format: 44 | * @code 45 | * MAJOR.MINOR.PATCH 46 | * @endcode 47 | */ 48 | #define NDN_NAC_VERSION_STRING "@VERSION_STRING@" 49 | 50 | /** 51 | * \brief ndn-nac version string, including git commit information if ndn-nac is built from a 52 | * specific git commit. 53 | * 54 | * #NDN_NAC_VERSION_BUILD_STRING is obtained using the following command (`ndn-nac-` prefix is 55 | * afterwards removed): ``git describe --match 'ndn-nac-*'`` 56 | * 57 | * When ndn-nac is not built from git, #NDN_NAC_VERSION_BUILD_STRING equals #NDN_NAC_VERSION_STRING. 58 | * 59 | * Format: 60 | * @code 61 | * MAJOR.MINOR.PATCH(-release-candidate-tag)(-(number-of-commits-since-tag)-COMMIT-HASH) 62 | * @endcode 63 | * 64 | * Example: 0.1.0-rc1-1-g5c86570 65 | */ 66 | #define NDN_NAC_VERSION_BUILD_STRING "@VERSION_BUILD@" 67 | 68 | #endif // NDN_NAC_VERSION_HPP 69 | -------------------------------------------------------------------------------- /tests/boost-test.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2020, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #ifndef NDN_NAC_TESTS_BOOST_TEST_HPP 21 | #define NDN_NAC_TESTS_BOOST_TEST_HPP 22 | 23 | // suppress warnings from Boost.Test 24 | #pragma GCC system_header 25 | #pragma clang system_header 26 | 27 | #define BOOST_TEST_DYN_LINK 28 | #include 29 | 30 | namespace ut = boost::unit_test; 31 | 32 | #endif // NDN_NAC_TESTS_BOOST_TEST_HPP 33 | -------------------------------------------------------------------------------- /tests/clock-fixture.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include "tests/clock-fixture.hpp" 21 | 22 | namespace ndn::nac::tests { 23 | 24 | ClockFixture::ClockFixture() 25 | : m_steadyClock(std::make_shared()) 26 | , m_systemClock(std::make_shared()) 27 | { 28 | time::setCustomClocks(m_steadyClock, m_systemClock); 29 | } 30 | 31 | ClockFixture::~ClockFixture() 32 | { 33 | time::setCustomClocks(nullptr, nullptr); 34 | } 35 | 36 | void 37 | ClockFixture::advanceClocks(time::nanoseconds tick, time::nanoseconds total) 38 | { 39 | BOOST_ASSERT(tick > time::nanoseconds::zero()); 40 | BOOST_ASSERT(total >= time::nanoseconds::zero()); 41 | 42 | while (total > time::nanoseconds::zero()) { 43 | auto t = std::min(tick, total); 44 | m_steadyClock->advance(t); 45 | m_systemClock->advance(t); 46 | total -= t; 47 | 48 | afterTick(); 49 | } 50 | } 51 | 52 | } // namespace ndn::nac::tests 53 | -------------------------------------------------------------------------------- /tests/clock-fixture.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #ifndef NAC_TESTS_CLOCK_FIXTURE_HPP 21 | #define NAC_TESTS_CLOCK_FIXTURE_HPP 22 | 23 | #include 24 | 25 | namespace ndn::nac::tests { 26 | 27 | /** 28 | * \brief A test fixture that overrides steady clock and system clock. 29 | */ 30 | class ClockFixture 31 | { 32 | public: 33 | virtual 34 | ~ClockFixture(); 35 | 36 | /** \brief Advance steady and system clocks. 37 | * 38 | * Clocks are advanced in increments of \p tick for \p nTicks ticks. 39 | * afterTick() is called after each tick. 40 | * 41 | * Exceptions thrown during I/O events are propagated to the caller. 42 | * Clock advancement will stop in the event of an exception. 43 | */ 44 | void 45 | advanceClocks(time::nanoseconds tick, size_t nTicks = 1) 46 | { 47 | advanceClocks(tick, tick * nTicks); 48 | } 49 | 50 | /** \brief Advance steady and system clocks. 51 | * 52 | * Clocks are advanced in increments of \p tick for \p total time. 53 | * The last increment might be shorter than \p tick. 54 | * afterTick() is called after each tick. 55 | * 56 | * Exceptions thrown during I/O events are propagated to the caller. 57 | * Clock advancement will stop in the event of an exception. 58 | */ 59 | void 60 | advanceClocks(time::nanoseconds tick, time::nanoseconds total); 61 | 62 | protected: 63 | ClockFixture(); 64 | 65 | private: 66 | /** \brief Called by advanceClocks() after each clock advancement (tick). 67 | * 68 | * The base class implementation is a no-op. 69 | */ 70 | virtual void 71 | afterTick() 72 | { 73 | } 74 | 75 | protected: 76 | std::shared_ptr m_steadyClock; 77 | std::shared_ptr m_systemClock; 78 | }; 79 | 80 | } // namespace ndn::nac::tests 81 | 82 | #endif // NAC_TESTS_CLOCK_FIXTURE_HPP 83 | -------------------------------------------------------------------------------- /tests/io-key-chain-fixture.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2023, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #ifndef NAC_TESTS_IO_KEY_CHAIN_FIXTURE_HPP 21 | #define NAC_TESTS_IO_KEY_CHAIN_FIXTURE_HPP 22 | 23 | #include "tests/clock-fixture.hpp" 24 | #include "tests/key-chain-fixture.hpp" 25 | 26 | #include 27 | 28 | namespace ndn::nac::tests { 29 | 30 | class IoKeyChainFixture : public ClockFixture, public KeyChainFixture 31 | { 32 | private: 33 | void 34 | afterTick() final 35 | { 36 | if (m_io.stopped()) { 37 | m_io.restart(); 38 | } 39 | m_io.poll(); 40 | } 41 | 42 | protected: 43 | boost::asio::io_context m_io; 44 | }; 45 | 46 | } // namespace ndn::nac::tests 47 | 48 | #endif // NAC_TESTS_IO_KEY_CHAIN_FIXTURE_HPP 49 | -------------------------------------------------------------------------------- /tests/key-chain-fixture.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2013-2022 Regents of the University of California. 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #ifndef NAC_TESTS_KEY_CHAIN_FIXTURE_HPP 21 | #define NAC_TESTS_KEY_CHAIN_FIXTURE_HPP 22 | 23 | #include 24 | 25 | namespace ndn::nac::tests { 26 | 27 | /** 28 | * @brief A fixture providing an in-memory KeyChain. 29 | */ 30 | class KeyChainFixture 31 | { 32 | protected: 33 | ndn::KeyChain m_keyChain{"pib-memory:", "tpm-memory:"}; 34 | }; 35 | 36 | } // namespace ndn::nac::tests 37 | 38 | #endif // NAC_TESTS_KEY_CHAIN_FIXTURE_HPP 39 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2020, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #define BOOST_TEST_MODULE NAC 21 | #include "tests/boost-test.hpp" 22 | -------------------------------------------------------------------------------- /tests/unit/access-manager.t.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include "access-manager.hpp" 21 | 22 | #include "tests/boost-test.hpp" 23 | #include "tests/io-key-chain-fixture.hpp" 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | namespace ndn::nac::tests { 31 | 32 | class AccessManagerFixture : public IoKeyChainFixture 33 | { 34 | public: 35 | AccessManagerFixture() 36 | : face(m_io, m_keyChain, {true, true}) 37 | , accessIdentity(m_keyChain.createIdentity("/access/policy/identity")) 38 | , nacIdentity(m_keyChain.createIdentity("/access/policy/identity/NAC/dataset", // hack to get access to KEK key-id 39 | RsaKeyParams())) 40 | , userIdentities{m_keyChain.createIdentity("/first/user", RsaKeyParams()), 41 | m_keyChain.createIdentity("/second/user", RsaKeyParams())} 42 | , manager(accessIdentity, Name("/dataset"), m_keyChain, face) 43 | { 44 | advanceClocks(1_ms, 10); 45 | 46 | for (auto& user : userIdentities) { 47 | manager.addMember(user.getDefaultKey().getDefaultCertificate()); 48 | } 49 | } 50 | 51 | public: 52 | DummyClientFace face; 53 | Identity accessIdentity; 54 | Identity nacIdentity; 55 | std::vector userIdentities; 56 | AccessManager manager; 57 | }; 58 | 59 | BOOST_FIXTURE_TEST_SUITE(TestAccessManager, AccessManagerFixture) 60 | 61 | BOOST_AUTO_TEST_CASE(PublishedKek) 62 | { 63 | face.receive(Interest(Name("/access/policy/identity/NAC/dataset/KEK")) 64 | .setCanBePrefix(true).setMustBeFresh(true)); 65 | advanceClocks(1_ms, 10); 66 | 67 | BOOST_CHECK_EQUAL(face.sentData.at(0).getName().getPrefix(-1), "/access/policy/identity/NAC/dataset/KEK"); 68 | BOOST_CHECK_EQUAL(face.sentData.at(0).getName().get(-1), nacIdentity.getDefaultKey().getName().get(-1)); 69 | } 70 | 71 | BOOST_AUTO_TEST_CASE(PublishedKdks) 72 | { 73 | for (auto& user : userIdentities) { 74 | Name kdk("/access/policy/identity/NAC/dataset/KDK"); 75 | kdk 76 | .append(nacIdentity.getDefaultKey().getName().get(-1)) 77 | .append("ENCRYPTED-BY") 78 | .append(user.getDefaultKey().getName()); 79 | 80 | face.receive(Interest(kdk).setCanBePrefix(true).setMustBeFresh(true)); 81 | advanceClocks(1_ms, 10); 82 | 83 | BOOST_CHECK_EQUAL(face.sentData.at(0).getName(), kdk); 84 | face.sentData.clear(); 85 | } 86 | } 87 | 88 | BOOST_AUTO_TEST_CASE(EnumerateDataFromIms) 89 | { 90 | BOOST_CHECK_EQUAL(manager.size(), 3); 91 | size_t nKek = 0; 92 | size_t nKdk = 0; 93 | for (const auto& data : manager) { 94 | BOOST_TEST_MESSAGE(data.getName()); 95 | if (data.getName().at(5) == KEK) { 96 | ++nKek; 97 | } 98 | else if (data.getName().at(5) == KDK) { 99 | ++nKdk; 100 | } 101 | } 102 | BOOST_CHECK_EQUAL(nKek, 1); 103 | BOOST_CHECK_EQUAL(nKdk, 2); 104 | } 105 | 106 | BOOST_AUTO_TEST_CASE(GenerateTestData, 107 | * ut::description("regenerates the static test data used by other test cases") 108 | * ut::disabled() 109 | * ut::label("generator")) 110 | { 111 | std::cerr << "const Block nacIdentity = \""; 112 | auto block = m_keyChain.exportSafeBag(nacIdentity.getDefaultKey().getDefaultCertificate(), 113 | "password", strlen("password"))->wireEncode(); 114 | printHex(std::cerr, block, true); 115 | std::cerr << "\"_block;\n"; 116 | 117 | std::cerr << "const std::vector userIdentities{\n"; 118 | for (const auto& userId : userIdentities) { 119 | std::cerr << " \""; 120 | block = m_keyChain.exportSafeBag(userId.getDefaultKey().getDefaultCertificate(), 121 | "password", strlen("password"))->wireEncode(); 122 | printHex(std::cerr, block, true); 123 | std::cerr << "\"_block,\n"; 124 | } 125 | std::cerr << "};\n"; 126 | 127 | std::cerr << "const std::vector managerPackets{\n"; 128 | for (const auto& data : manager) { 129 | std::cerr << " \""; 130 | printHex(std::cerr, data.wireEncode(), true); 131 | std::cerr << "\"_block,\n"; 132 | } 133 | std::cerr << "};\n"; 134 | } 135 | 136 | BOOST_AUTO_TEST_SUITE_END() 137 | 138 | } // namespace ndn::nac::tests 139 | -------------------------------------------------------------------------------- /tests/unit/common.t.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, Regents of the University of California 4 | * 5 | * This file is part of NAC (Name-Based Access Control for NDN). 6 | * See AUTHORS.md for complete list of NAC authors and contributors. 7 | * 8 | * NAC is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * NAC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * NAC, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #include "common.hpp" 21 | 22 | #include "tests/boost-test.hpp" 23 | 24 | namespace ndn::nac::tests { 25 | 26 | BOOST_AUTO_TEST_SUITE(TestCommon) 27 | 28 | BOOST_AUTO_TEST_CASE(Helpers) 29 | { 30 | bool hasFailed = false; 31 | auto onFailed = [&] (auto&&...) { hasFailed = true; }; 32 | 33 | auto kdkPrefix = convertKekNameToKdkPrefix(Name("/access/prefix/NAC/dataset/KEK/id"), onFailed); 34 | 35 | BOOST_CHECK(!hasFailed); 36 | BOOST_CHECK(kdkPrefix == Name("/access/prefix/NAC/dataset/KDK/id")); 37 | 38 | hasFailed = false; 39 | kdkPrefix = convertKekNameToKdkPrefix(Name("/invalid/name"), onFailed); 40 | BOOST_CHECK(hasFailed); 41 | BOOST_CHECK(kdkPrefix.empty()); 42 | 43 | hasFailed = false; 44 | Name kdkIdentity, kdkKeyName; 45 | std::tie(kdkPrefix, kdkIdentity, kdkKeyName) = 46 | extractKdkInfoFromCkName(Name("/ck/prefix/stuff/ENCRYPTED-BY/access/prefix/NAC/dataset/KEK/id"), 47 | Name("/ck/prefix/stuff"), onFailed); 48 | BOOST_CHECK(!hasFailed); 49 | BOOST_CHECK_EQUAL(kdkPrefix, Name("/access/prefix/NAC/dataset/KDK/id")); 50 | BOOST_CHECK_EQUAL(kdkIdentity, Name("/access/prefix/NAC/dataset")); 51 | BOOST_CHECK_EQUAL(kdkKeyName, Name("/access/prefix/NAC/dataset/KEY/id")); 52 | 53 | hasFailed = false; 54 | std::tie(kdkPrefix, kdkIdentity, kdkKeyName) = 55 | extractKdkInfoFromCkName(Name("/ck/prefix/ENCRYPTED-BY/access/prefix/NAC/dataset/KEK/id"), 56 | Name("/ck/prefix/stuff"), onFailed); 57 | BOOST_CHECK(hasFailed); 58 | BOOST_CHECK(kdkPrefix.empty()); 59 | } 60 | 61 | BOOST_AUTO_TEST_SUITE_END() 62 | 63 | } // namespace ndn::nac::tests 64 | -------------------------------------------------------------------------------- /tests/unit/decryptor.t.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include "decryptor.hpp" 21 | 22 | #include "access-manager.hpp" 23 | #include "encrypted-content.hpp" 24 | #include "encryptor.hpp" 25 | 26 | #include "tests/boost-test.hpp" 27 | #include "tests/io-key-chain-fixture.hpp" 28 | #include "tests/unit/static-data.hpp" 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | namespace ndn::nac::tests { 36 | 37 | class DecryptorStaticDataEnvironment : public IoKeyChainFixture 38 | { 39 | public: 40 | DecryptorStaticDataEnvironment() 41 | { 42 | StaticData data; 43 | for (const auto& block : data.managerPackets) { 44 | m_ims.insert(*std::make_shared(block)); 45 | } 46 | for (const auto& block : data.encryptorPackets) { 47 | m_ims.insert(*std::make_shared(block)); 48 | } 49 | 50 | auto serveFromIms = [this] (const Name&, const Interest& interest) { 51 | auto data = m_ims.find(interest); 52 | if (data != nullptr) { 53 | m_imsFace.put(*data); 54 | } 55 | }; 56 | m_imsFace.setInterestFilter("/", serveFromIms, [] (auto...) {}); 57 | advanceClocks(1_ms, 10); 58 | 59 | // import "/first/user" identity 60 | m_keyChain.importSafeBag(SafeBag(data.userIdentities.at(0)), "password", strlen("password")); 61 | // credentialIdentity = m_keyChain.getPib().getIdentity("/first/user"); 62 | 63 | m_keyChain.createIdentity("/not/authorized"); 64 | } 65 | 66 | protected: 67 | DummyClientFace m_imsFace{m_io, m_keyChain, {false, true}}; 68 | 69 | private: 70 | InMemoryStoragePersistent m_ims; 71 | }; 72 | 73 | template 74 | class DecryptorFixture : public DecryptorStaticDataEnvironment 75 | { 76 | public: 77 | DecryptorFixture() 78 | : face(m_io, m_keyChain, {false, true}) 79 | , decryptor(m_keyChain.getPib().getIdentity(T().identity).getDefaultKey(), validator, m_keyChain, face) 80 | { 81 | face.linkTo(m_imsFace); 82 | advanceClocks(1_ms, 10); 83 | } 84 | 85 | public: 86 | DummyClientFace face; 87 | security::ValidatorNull validator; 88 | Decryptor decryptor; 89 | }; 90 | 91 | BOOST_AUTO_TEST_SUITE(TestDecryptor) 92 | 93 | struct Valid 94 | { 95 | std::string identity = "/first/user"; 96 | bool expectToSucceed = true; 97 | }; 98 | 99 | struct Invalid 100 | { 101 | std::string identity = "/not/authorized"; 102 | bool expectToSucceed = false; 103 | }; 104 | 105 | using Identities = boost::mp11::mp_list; 106 | 107 | BOOST_FIXTURE_TEST_CASE_TEMPLATE(DecryptSuccess, T, Identities, DecryptorFixture) 108 | { 109 | StaticData data; 110 | 111 | size_t nSuccesses = 0; 112 | size_t nFailures = 0; 113 | this->decryptor.decrypt(data.encryptedBlobs.at(0), 114 | [&] (ConstBufferPtr buffer) { 115 | ++nSuccesses; 116 | BOOST_CHECK_EQUAL(buffer->size(), 15); 117 | std::string content(buffer->get(), buffer->size()); 118 | BOOST_CHECK_EQUAL(content, "Data to encrypt"); 119 | }, 120 | [&] (const ErrorCode&, const std::string& msg) { 121 | BOOST_TEST_MESSAGE(msg); 122 | ++nFailures; 123 | }); 124 | this->advanceClocks(2_s, 10); 125 | 126 | BOOST_CHECK_EQUAL(nSuccesses, T().expectToSucceed ? 1 : 0); 127 | BOOST_CHECK_EQUAL(nFailures, T().expectToSucceed ? 0 : 1); 128 | } 129 | 130 | BOOST_AUTO_TEST_SUITE_END() 131 | 132 | } // namespace ndn::nac::tests 133 | -------------------------------------------------------------------------------- /tests/unit/encrypted-content.t.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, Regents of the University of California 4 | * 5 | * This file is part of NAC (Name-Based Access Control for NDN). 6 | * See AUTHORS.md for complete list of NAC authors and contributors. 7 | * 8 | * NAC is free software: you can redistribute it and/or modify it under the terms 9 | * of the GNU General Public License as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * 12 | * NAC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 13 | * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 14 | * PURPOSE. See the GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * NAC, e.g., in COPYING.md file. If not, see . 18 | */ 19 | 20 | #include "encrypted-content.hpp" 21 | 22 | #include "tests/boost-test.hpp" 23 | 24 | namespace ndn::nac::tests { 25 | 26 | class EncryptedContentFixture 27 | { 28 | public: 29 | EncryptedContentFixture() 30 | { 31 | BOOST_ASSERT(randomBlock.value_size() == 3); 32 | BOOST_ASSERT(randomBuffer->size() == 10); 33 | } 34 | 35 | public: 36 | EncryptedContent content; 37 | Block randomBlock = "01 03 000000"_block; 38 | ConstBufferPtr randomBuffer = std::make_shared(10); 39 | }; 40 | 41 | BOOST_FIXTURE_TEST_SUITE(TestEncryptedContent, EncryptedContentFixture) 42 | 43 | BOOST_AUTO_TEST_CASE(Constructor) 44 | { 45 | BOOST_CHECK_THROW(content.wireEncode(), tlv::Error); 46 | content.setPayload(randomBlock); 47 | 48 | BOOST_CHECK(!content.hasIv()); 49 | BOOST_CHECK(!content.hasPayloadKey()); 50 | BOOST_CHECK(!content.hasKeyLocator()); 51 | 52 | BOOST_CHECK_EQUAL(content.wireEncode(), "82 07 84050103000000"_block); 53 | 54 | content.setIv(randomBlock); 55 | BOOST_CHECK_EQUAL(content.wireEncode(), "82[0E]=8405010300000085050103000000"_block); 56 | 57 | content.setKeyLocator("/random/name"); 58 | BOOST_CHECK_EQUAL(content.wireEncode(), "82[1E]=8405010300000085050103000000070E080672616E646F6D08046E616D65"_block); 59 | 60 | content = EncryptedContent("82 07 84050103000000"_block); 61 | BOOST_CHECK(!content.hasIv()); 62 | BOOST_CHECK(!content.hasPayloadKey()); 63 | BOOST_CHECK(!content.hasKeyLocator()); 64 | 65 | content = EncryptedContent("82 1E 8505010300000084050103000000070E080672616E646F6D08046E616D65"_block); 66 | BOOST_CHECK(content.hasIv()); 67 | BOOST_CHECK(!content.hasPayloadKey()); 68 | BOOST_CHECK(content.hasKeyLocator()); 69 | } 70 | 71 | BOOST_AUTO_TEST_SUITE(SetterGetter) 72 | 73 | BOOST_AUTO_TEST_CASE(Iv) 74 | { 75 | content.setPayload(randomBlock); 76 | 77 | content.setIv(randomBlock); 78 | BOOST_REQUIRE(content.hasIv()); 79 | BOOST_CHECK_EQUAL(content.getIv().type(), tlv::InitializationVector); 80 | BOOST_CHECK_EQUAL(content.getIv().blockFromValue(), randomBlock); 81 | 82 | content.unsetIv(); 83 | BOOST_CHECK(!content.hasIv()); 84 | 85 | content.setIv(randomBuffer); 86 | BOOST_REQUIRE(content.hasIv()); 87 | BOOST_CHECK_EQUAL(content.getIv().type(), tlv::InitializationVector); 88 | BOOST_CHECK_THROW(content.getIv().blockFromValue(), tlv::Error); 89 | BOOST_CHECK_EQUAL(content.getIv().value_size(), randomBuffer->size()); 90 | 91 | content = EncryptedContent("82[13]=84050103000000850A00000000000000000000"_block); 92 | BOOST_REQUIRE(content.hasIv()); 93 | BOOST_CHECK_EQUAL(content.getIv().type(), tlv::InitializationVector); 94 | BOOST_CHECK_THROW(content.getIv().blockFromValue(), tlv::Error); 95 | BOOST_CHECK_EQUAL(content.getIv().value_size(), randomBuffer->size()); 96 | } 97 | 98 | BOOST_AUTO_TEST_CASE(Payload) 99 | { 100 | content.setPayload(randomBlock); 101 | BOOST_CHECK_EQUAL(content.getPayload().type(), tlv::EncryptedPayload); 102 | BOOST_CHECK_EQUAL(content.getPayload().blockFromValue(), randomBlock); 103 | 104 | content.setPayload(randomBuffer); 105 | BOOST_CHECK_EQUAL(content.getPayload().type(), tlv::EncryptedPayload); 106 | BOOST_CHECK_THROW(content.getPayload().blockFromValue(), tlv::Error); 107 | BOOST_CHECK_EQUAL(content.getPayload().value_size(), randomBuffer->size()); 108 | 109 | content = EncryptedContent("82[0C]=840A00000000000000000000"_block); 110 | BOOST_CHECK_EQUAL(content.getPayload().type(), tlv::EncryptedPayload); 111 | BOOST_CHECK_THROW(content.getPayload().blockFromValue(), tlv::Error); 112 | BOOST_CHECK_EQUAL(content.getPayload().value_size(), randomBuffer->size()); 113 | } 114 | 115 | BOOST_AUTO_TEST_CASE(PayloadKey) 116 | { 117 | content.setPayload(randomBlock); 118 | 119 | content.setPayloadKey(randomBlock); 120 | BOOST_REQUIRE(content.hasPayloadKey()); 121 | BOOST_CHECK_EQUAL(content.getPayloadKey().type(), tlv::EncryptedPayloadKey); 122 | BOOST_CHECK_EQUAL(content.getPayloadKey().blockFromValue(), randomBlock); 123 | 124 | content.unsetPayloadKey(); 125 | BOOST_CHECK(!content.hasPayloadKey()); 126 | 127 | content.setPayloadKey(randomBuffer); 128 | BOOST_REQUIRE(content.hasPayloadKey()); 129 | BOOST_CHECK_EQUAL(content.getPayloadKey().type(), tlv::EncryptedPayloadKey); 130 | BOOST_CHECK_THROW(content.getPayloadKey().blockFromValue(), tlv::Error); 131 | BOOST_CHECK_EQUAL(content.getPayloadKey().value_size(), randomBuffer->size()); 132 | 133 | content = EncryptedContent("82[13]=84050103000000860A00000000000000000000"_block); 134 | BOOST_CHECK_EQUAL(content.getPayloadKey().type(), tlv::EncryptedPayloadKey); 135 | BOOST_CHECK_THROW(content.getPayloadKey().blockFromValue(), tlv::Error); 136 | BOOST_CHECK_EQUAL(content.getPayloadKey().value_size(), randomBuffer->size()); 137 | } 138 | 139 | BOOST_AUTO_TEST_CASE(KeyLocator) 140 | { 141 | content.setPayload(randomBlock); 142 | 143 | content.setKeyLocator("/random/name"); 144 | BOOST_REQUIRE(content.hasKeyLocator()); 145 | BOOST_CHECK_EQUAL(content.getKeyLocator(), "/random/name"); 146 | 147 | content.unsetPayloadKey(); 148 | BOOST_CHECK(!content.hasPayloadKey()); 149 | 150 | content = EncryptedContent("82[17]=84050103000000070E080672616E646F6D08046E616D65"_block); 151 | BOOST_REQUIRE(content.hasKeyLocator()); 152 | BOOST_CHECK_EQUAL(content.getKeyLocator(), "/random/name"); 153 | } 154 | 155 | BOOST_AUTO_TEST_SUITE_END() // SetterGetter 156 | 157 | BOOST_AUTO_TEST_SUITE_END() 158 | 159 | } // namespace ndn::nac::tests 160 | -------------------------------------------------------------------------------- /tests/unit/encryptor.t.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2024, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include "encryptor.hpp" 21 | 22 | #include "tests/boost-test.hpp" 23 | #include "tests/io-key-chain-fixture.hpp" 24 | #include "tests/unit/static-data.hpp" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | namespace ndn::nac::tests { 34 | 35 | class EncryptorStaticDataEnvironment : public IoKeyChainFixture 36 | { 37 | public: 38 | EncryptorStaticDataEnvironment(bool shouldPublishData) 39 | { 40 | if (shouldPublishData) { 41 | publishData(); 42 | } 43 | 44 | auto serveFromIms = [this] (const Name&, const Interest& interest) { 45 | auto data = m_ims.find(interest); 46 | if (data != nullptr) { 47 | m_imsFace.put(*data); 48 | } 49 | }; 50 | m_imsFace.setInterestFilter("/", serveFromIms, [] (auto...) {}); 51 | advanceClocks(1_ms, 10); 52 | 53 | m_imsFace.sentData.clear(); 54 | m_imsFace.sentInterests.clear(); 55 | } 56 | 57 | void 58 | publishData() 59 | { 60 | StaticData data; 61 | for (const auto& block : data.managerPackets) { 62 | m_ims.insert(*std::make_shared(block)); 63 | } 64 | advanceClocks(1_ms, 10); 65 | } 66 | 67 | protected: 68 | DummyClientFace m_imsFace{m_io, m_keyChain, {true, true}}; 69 | 70 | private: 71 | InMemoryStoragePersistent m_ims; 72 | }; 73 | 74 | template 75 | class EncryptorFixture : public EncryptorStaticDataEnvironment 76 | { 77 | public: 78 | EncryptorFixture() 79 | : EncryptorStaticDataEnvironment(shouldPublishData) 80 | , face(m_io, m_keyChain, {true, true}) 81 | , encryptor("/access/policy/identity/NAC/dataset", "/some/ck/prefix", signingWithSha256(), 82 | [=] (const ErrorCode& code, const std::string& error) { 83 | onFailure(code, error); 84 | }, 85 | validator, m_keyChain, face) 86 | { 87 | face.linkTo(m_imsFace); 88 | advanceClocks(1_ms, 10); 89 | } 90 | 91 | public: 92 | DummyClientFace face; 93 | security::ValidatorNull validator; 94 | Encryptor encryptor; 95 | signal::Signal onFailure; 96 | }; 97 | 98 | BOOST_FIXTURE_TEST_SUITE(TestEncryptor, EncryptorFixture<>) 99 | 100 | BOOST_AUTO_TEST_CASE(EncryptAndPublishedCk) 101 | { 102 | encryptor.m_kek.reset(); 103 | BOOST_CHECK_EQUAL(encryptor.m_isKekRetrievalInProgress, false); 104 | encryptor.regenerateCk(); 105 | BOOST_CHECK_EQUAL(encryptor.m_isKekRetrievalInProgress, true); 106 | 107 | const std::string plaintext = "Data to encrypt"; 108 | auto block = encryptor.encrypt({reinterpret_cast(plaintext.data()), plaintext.size()}); 109 | 110 | EncryptedContent content(block); 111 | auto ckPrefix = content.getKeyLocator(); 112 | BOOST_CHECK_EQUAL(ckPrefix.getPrefix(-1), "/some/ck/prefix/CK"); 113 | 114 | BOOST_CHECK(content.hasIv()); 115 | BOOST_CHECK_NE(std::string(reinterpret_cast(content.getPayload().value()), 116 | content.getPayload().value_size()), 117 | plaintext); 118 | 119 | advanceClocks(1_ms, 10); 120 | 121 | // check that KEK interests has been sent 122 | BOOST_CHECK_EQUAL(face.sentInterests.at(0).getName().getPrefix(6), 123 | Name("/access/policy/identity/NAC/dataset/KEK")); 124 | 125 | auto kek = m_imsFace.sentData.at(0); 126 | BOOST_CHECK_EQUAL(kek.getName().getPrefix(6), Name("/access/policy/identity/NAC/dataset/KEK")); 127 | BOOST_CHECK_EQUAL(kek.getName().size(), 7); 128 | 129 | face.sentData.clear(); 130 | face.sentInterests.clear(); 131 | 132 | face.receive(Interest(ckPrefix) 133 | .setCanBePrefix(true).setMustBeFresh(true)); 134 | advanceClocks(1_ms, 10); 135 | 136 | auto ckName = face.sentData.at(0).getName(); 137 | BOOST_CHECK_EQUAL(ckName.getPrefix(4), "/some/ck/prefix/CK"); 138 | BOOST_CHECK_EQUAL(ckName.get(5), name::Component("ENCRYPTED-BY")); 139 | 140 | auto extractedKek = ckName.getSubName(6); 141 | BOOST_CHECK_EQUAL(extractedKek, kek.getName()); 142 | 143 | BOOST_CHECK_EQUAL(encryptor.m_isKekRetrievalInProgress, false); 144 | } 145 | 146 | BOOST_FIXTURE_TEST_CASE(KekRetrievalFailure, EncryptorFixture) 147 | { 148 | size_t nErrors = 0; 149 | onFailure.connect([&] (auto&&...) { ++nErrors; }); 150 | 151 | const std::string plaintext = "Data to encrypt"; 152 | auto block = encryptor.encrypt({reinterpret_cast(plaintext.data()), plaintext.size()}); 153 | advanceClocks(1_ms, 10); 154 | 155 | // check that KEK interests has been sent 156 | BOOST_CHECK_EQUAL(face.sentInterests.at(0).getName().getPrefix(6), Name("/access/policy/identity/NAC/dataset/KEK")); 157 | 158 | // and failed 159 | BOOST_CHECK_EQUAL(m_imsFace.sentData.size(), 0); 160 | 161 | advanceClocks(1_s, 13); // 4_s default interest lifetime x 3 162 | BOOST_CHECK_EQUAL(nErrors, 1); 163 | BOOST_CHECK_EQUAL(m_imsFace.sentData.size(), 0); 164 | 165 | advanceClocks(1_s, 730); // 60 seconds between attempts + ~12 seconds for each attempt 166 | BOOST_CHECK_EQUAL(nErrors, 11); 167 | BOOST_CHECK_EQUAL(m_imsFace.sentData.size(), 0); 168 | 169 | // check recovery 170 | 171 | publishData(); 172 | 173 | advanceClocks(1_s, 73); 174 | 175 | auto kek = m_imsFace.sentData.at(0); 176 | BOOST_CHECK_EQUAL(kek.getName().getPrefix(6), Name("/access/policy/identity/NAC/dataset/KEK")); 177 | BOOST_CHECK_EQUAL(kek.getName().size(), 7); 178 | } 179 | 180 | BOOST_AUTO_TEST_CASE(EnumerateDataFromIms) 181 | { 182 | encryptor.regenerateCk(); 183 | advanceClocks(1_ms, 10); 184 | 185 | encryptor.regenerateCk(); 186 | advanceClocks(1_ms, 10); 187 | 188 | BOOST_CHECK_EQUAL(encryptor.size(), 3); 189 | size_t nCk = 0; 190 | for (const auto& data : encryptor) { 191 | BOOST_TEST_MESSAGE(data.getName()); 192 | if (data.getName().getPrefix(4) == Name("/some/ck/prefix/CK")) { 193 | ++nCk; 194 | } 195 | } 196 | BOOST_CHECK_EQUAL(nCk, 3); 197 | } 198 | 199 | BOOST_AUTO_TEST_CASE(GenerateTestData, 200 | * ut::description("regenerates the static test data used by other test cases") 201 | * ut::disabled() 202 | * ut::label("generator")) 203 | { 204 | const auto plaintext = "Data to encrypt"s; 205 | 206 | std::cerr << "const std::vector encryptedBlobs{\n"; 207 | for (size_t i = 0; i < 3; ++i) { 208 | std::cerr << " \""; 209 | auto block = encryptor.encrypt({reinterpret_cast(plaintext.data()), plaintext.size()}); 210 | printHex(std::cerr, block.wireEncode(), true); 211 | std::cerr << "\"_block,\n"; 212 | 213 | encryptor.regenerateCk(); 214 | advanceClocks(1_ms, 10); 215 | } 216 | std::cerr << "};\n"; 217 | 218 | std::cerr << "const std::vector encryptorPackets{\n"; 219 | for (const auto& data : encryptor) { 220 | std::cerr << " \""; 221 | printHex(std::cerr, data.wireEncode(), true); 222 | std::cerr << "\"_block,\n"; 223 | } 224 | std::cerr << "};\n"; 225 | } 226 | 227 | BOOST_AUTO_TEST_SUITE_END() 228 | 229 | } // namespace ndn::nac::tests 230 | -------------------------------------------------------------------------------- /tests/wscript: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- 2 | 3 | top = '..' 4 | 5 | def build(bld): 6 | bld.program( 7 | target=f'{top}/unit-tests', 8 | name='unit-tests', 9 | source=bld.path.ant_glob('**/*.cpp'), 10 | use='BOOST_TESTS libndn-nac', 11 | includes=top, 12 | install_path=None) 13 | -------------------------------------------------------------------------------- /tools/ndn-nac/add-member.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2023, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include "ndn-nac.hpp" 21 | #include "access-manager.hpp" 22 | 23 | namespace ndn::nac { 24 | 25 | int 26 | nac_add_member(int argc, char** argv) 27 | { 28 | namespace po = boost::program_options; 29 | 30 | Name identityName; 31 | Name datasetName; 32 | std::string output; 33 | std::string member; 34 | 35 | po::options_description description("General Usage\n" 36 | " ndn-nac add-member [-h] [-o output] [-d dataset] [-i] identity [-m] memberCert\n" 37 | "General options"); 38 | description.add_options() 39 | ("help,h", "Produce help message") 40 | ("output,o", po::value(&output), "(Optional) output file, stdout if not specified") 41 | ("identity,i", po::value(&identityName), "Data owner's namespace identity") 42 | ("dataset,d", po::value(&datasetName), "Name of dataset to control") 43 | ("member,m", po::value(&member), "File with member's certificate, stdin if -") 44 | ; 45 | 46 | po::positional_options_description p; 47 | p.add("identity", 1); 48 | p.add("member", 1); 49 | 50 | po::variables_map vm; 51 | try { 52 | po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm); 53 | po::notify(vm); 54 | } 55 | catch (const std::exception& e) { 56 | std::cerr << "ERROR: " << e.what() << std::endl; 57 | std::cerr << description << std::endl; 58 | return 1; 59 | } 60 | 61 | if (vm.count("help") != 0) { 62 | std::cerr << description << std::endl; 63 | return 0; 64 | } 65 | 66 | if (vm.count("identity") == 0) { 67 | std::cerr << "ERROR: identity must be specified" << std::endl; 68 | std::cerr << description << std::endl; 69 | return 1; 70 | } 71 | 72 | if (vm.count("member") == 0) { 73 | std::cerr << "ERROR: member must be specified" << std::endl; 74 | std::cerr << description << std::endl; 75 | return 1; 76 | } 77 | 78 | if (vm.count("output") == 0) 79 | output = "-"; 80 | 81 | try { 82 | KeyChain keyChain; 83 | Identity id = keyChain.getPib().getIdentity(identityName); 84 | 85 | auto cert = loadCertificate(member); 86 | 87 | DummyClientFace face(keyChain); // to avoid any real IO 88 | AccessManager manager(id, datasetName, keyChain, face); 89 | 90 | auto kdk = manager.addMember(cert); 91 | 92 | if (output == "-") 93 | io::save(kdk, std::cout); 94 | else 95 | io::save(kdk, output); 96 | 97 | return 0; 98 | } 99 | catch (const std::runtime_error& e) { 100 | std::cerr << "ERROR: " << e.what() << std::endl; 101 | return 1; 102 | } 103 | } 104 | 105 | } // namespace ndn::nac 106 | -------------------------------------------------------------------------------- /tools/ndn-nac/dump-kek.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2023, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include "ndn-nac.hpp" 21 | #include "access-manager.hpp" 22 | 23 | namespace ndn::nac { 24 | 25 | int 26 | nac_dump_kek(int argc, char** argv) 27 | { 28 | namespace po = boost::program_options; 29 | 30 | Name identityName; 31 | Name datasetName; 32 | std::string output; 33 | 34 | po::options_description description("General Usage\n" 35 | " ndn-nac dump-kek [-h] [-o output] [-d dataset] [-i] identity \n" 36 | "General options"); 37 | description.add_options() 38 | ("help,h", "Produce help message") 39 | ("output,o", po::value(&output), "(Optional) output file, stdout if not specified") 40 | ("identity,i", po::value(&identityName), "Data owner's namespace identity") 41 | ("dataset,d", po::value(&datasetName), "Name of dataset to control") 42 | ; 43 | 44 | po::positional_options_description p; 45 | p.add("identity", 1); 46 | 47 | po::variables_map vm; 48 | try { 49 | po::store(po::command_line_parser(argc, argv).options(description).positional(p).run(), vm); 50 | po::notify(vm); 51 | } 52 | catch (const std::exception& e) { 53 | std::cerr << "ERROR: " << e.what() << std::endl; 54 | std::cerr << description << std::endl; 55 | return 1; 56 | } 57 | 58 | if (vm.count("help") != 0) { 59 | std::cerr << description << std::endl; 60 | return 0; 61 | } 62 | 63 | if (vm.count("identity") == 0) { 64 | std::cerr << "ERROR: identity must be specified" << std::endl; 65 | std::cerr << description << std::endl; 66 | return 1; 67 | } 68 | 69 | if (vm.count("output") == 0) 70 | output = "-"; 71 | 72 | try { 73 | KeyChain keyChain; 74 | Identity id = keyChain.getPib().getIdentity(identityName); 75 | 76 | DummyClientFace face(keyChain); // to avoid any real IO 77 | AccessManager manager(id, datasetName, keyChain, face); 78 | 79 | if (manager.size() != 1) { 80 | std::cerr << "ERROR: Incorrect state of AccessManager instance (expect 1 KDK)" << std::endl; 81 | return 2; 82 | } 83 | 84 | if (output == "-") 85 | io::save(*manager.begin(), std::cout); 86 | else 87 | io::save(*manager.begin(), output); 88 | 89 | return 0; 90 | } 91 | catch (const std::runtime_error& e) { 92 | std::cerr << "ERROR: " << e.what() << std::endl; 93 | return 1; 94 | } 95 | } 96 | 97 | } // namespace ndn::nac 98 | -------------------------------------------------------------------------------- /tools/ndn-nac/main.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2020, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #include "ndn-nac.hpp" 21 | #include "version.hpp" 22 | 23 | #include 24 | 25 | #include 26 | 27 | NDN_LOG_INIT(nac.Cmd); 28 | 29 | const char NAC_HELP_TEXT[] = R"STR( 30 | help Show all commands 31 | version Show version and exit 32 | dump-kek Dump KEK 33 | add-member Create KDK for the member 34 | )STR"; 35 | 36 | int 37 | main(int argc, char** argv) 38 | { 39 | if (argc < 2) { 40 | std::cerr << NAC_HELP_TEXT << std::endl; 41 | return 2; 42 | } 43 | 44 | using namespace ndn::nac; 45 | 46 | std::string command(argv[1]); 47 | try { 48 | if (command == "help") { std::cout << NAC_HELP_TEXT << std::endl; } 49 | else if (command == "version") { std::cout << NDN_NAC_VERSION_BUILD_STRING << std::endl; } 50 | else if (command == "dump-kek") { return nac_dump_kek(argc - 1, argv + 1); } 51 | else if (command == "add-member") { return nac_add_member(argc - 1, argv + 1); } 52 | else { 53 | std::cerr << "ERROR: Unknown command '" << command << "'\n" 54 | << "\n" 55 | << NAC_HELP_TEXT << std::endl; 56 | return 2; 57 | } 58 | } 59 | catch (const std::exception& e) { 60 | std::cerr << "ERROR: " << e.what() << std::endl; 61 | NDN_LOG_ERROR(boost::diagnostic_information(e)); 62 | return 1; 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /tools/ndn-nac/ndn-nac.hpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ 2 | /* 3 | * Copyright (c) 2014-2022, Regents of the University of California 4 | * 5 | * NAC library is free software: you can redistribute it and/or modify it under the 6 | * terms of the GNU Lesser General Public License as published by the Free Software 7 | * Foundation, either version 3 of the License, or (at your option) any later version. 8 | * 9 | * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received copies of the GNU General Public License and GNU Lesser 14 | * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see 15 | * . 16 | * 17 | * See AUTHORS.md for complete list of NAC library authors and contributors. 18 | */ 19 | 20 | #ifndef NAC_TOOLS_NDN_NAC_NDN_NAC_HPP 21 | #define NAC_TOOLS_NDN_NAC_NDN_NAC_HPP 22 | 23 | #include "common.hpp" 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | namespace ndn::nac { 36 | 37 | int 38 | nac_dump_kek(int argc, char** argv); 39 | 40 | int 41 | nac_add_member(int argc, char** argv); 42 | 43 | inline Certificate 44 | loadCertificate(const std::string& fileName) 45 | { 46 | try { 47 | if (fileName == "-") { 48 | return io::loadTlv(std::cin, io::BASE64); 49 | } 50 | 51 | std::ifstream file(fileName); 52 | if (!file) { 53 | NDN_THROW(std::runtime_error("Cannot open '" + fileName + "'")); 54 | } 55 | return io::loadTlv(file, io::BASE64); 56 | } 57 | catch (const io::Error& e) { 58 | NDN_THROW_NESTED(std::runtime_error("Cannot load certificate from '" + fileName + 59 | "': malformed TLV or not in base64 format (" + e.what() + ")")); 60 | } 61 | } 62 | 63 | } // namespace ndn::nac 64 | 65 | #endif // NAC_TOOLS_NDN_NAC_NDN_NAC_HPP 66 | -------------------------------------------------------------------------------- /tools/wscript: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- 2 | 3 | top = '..' 4 | 5 | def build(bld): 6 | # Single object tools: 7 | # tools/foo.cpp is a self-contained tool with a main() function 8 | # and is built as build/bin/foo. These tools cannot be unit-tested. 9 | for tool in bld.path.ant_glob('*.cpp'): 10 | name = tool.change_ext('').path_from(bld.path.get_bld()) 11 | bld.program(name=name, 12 | target=f'{top}/bin/{name}', 13 | source=[tool], 14 | use='BOOST_TOOLS libndn-nac') 15 | 16 | # Sub-directory tools: 17 | # tools/foo/**/*.cpp are compiled and linked into build/bin/foo. 18 | # tools/foo/main.cpp must exist and must contain the main() function. 19 | # All other objects are collected into 'tools-objects' and can be unit-tested. 20 | testableObjects = [] 21 | for subdir in bld.path.ant_glob('*', dir=True, src=False): 22 | name = subdir.path_from(bld.path) 23 | subWscript = subdir.find_node('wscript') 24 | if subWscript: 25 | # if the subdir has a wscript, delegate to it 26 | bld.recurse(name) 27 | continue 28 | 29 | mainFile = subdir.find_node('main.cpp') 30 | if mainFile is None: 31 | # not a C++ tool, skip the subdir 32 | continue 33 | 34 | srcFiles = subdir.ant_glob('**/*.cpp', excl=['main.cpp']) 35 | srcObjects = '' 36 | if srcFiles: 37 | srcObjects = f'tools-{name}-objects' 38 | bld.objects(target=srcObjects, 39 | source=srcFiles, 40 | use='libndn-nac', 41 | includes=name) 42 | testableObjects.append(srcObjects) 43 | 44 | bld.program(name=name, 45 | target=f'{top}/bin/{name}', 46 | source=[mainFile], 47 | use=f'BOOST_TOOLS libndn-nac {srcObjects}', 48 | includes=name) 49 | 50 | bld.objects(target='tools-objects', 51 | source=[], 52 | export_includes='.', 53 | use=testableObjects) 54 | -------------------------------------------------------------------------------- /waf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/name-based-access-control/181a4de3256a86121d6980b334a7a40c4287fe60/waf -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- 2 | 3 | import os 4 | import subprocess 5 | from waflib import Context, Logs 6 | 7 | VERSION = '0.1.0' 8 | APPNAME = 'ndn-nac' 9 | GIT_TAG_PREFIX = 'ndn-nac-' 10 | 11 | def options(opt): 12 | opt.load(['compiler_cxx', 'gnu_dirs']) 13 | opt.load(['default-compiler-flags', 14 | 'coverage', 'sanitizers', 'boost', 15 | 'doxygen', 'sphinx'], 16 | tooldir=['.waf-tools']) 17 | 18 | optgrp = opt.add_option_group('NAC Options') 19 | optgrp.add_option('--with-examples', action='store_true', default=False, 20 | help='Build examples') 21 | optgrp.add_option('--with-tests', action='store_true', default=False, 22 | help='Build unit tests') 23 | optgrp.add_option('--without-tools', action='store_false', default=True, dest='with_tools', 24 | help='Do not build tools') 25 | 26 | def configure(conf): 27 | conf.load(['compiler_cxx', 'gnu_dirs', 28 | 'default-compiler-flags', 'boost', 29 | 'doxygen', 'sphinx']) 30 | 31 | conf.env.WITH_EXAMPLES = conf.options.with_examples 32 | conf.env.WITH_TESTS = conf.options.with_tests 33 | conf.env.WITH_TOOLS = conf.options.with_tools 34 | 35 | conf.find_program('dot', mandatory=False) 36 | 37 | # Prefer pkgconf if it's installed, because it gives more correct results 38 | # on Fedora/CentOS/RHEL/etc. See https://bugzilla.redhat.com/show_bug.cgi?id=1953348 39 | # Store the result in env.PKGCONFIG, which is the variable used inside check_cfg() 40 | conf.find_program(['pkgconf', 'pkg-config'], var='PKGCONFIG') 41 | 42 | pkg_config_path = os.environ.get('PKG_CONFIG_PATH', f'{conf.env.LIBDIR}/pkgconfig') 43 | conf.check_cfg(package='libndn-cxx', args=['libndn-cxx >= 0.8.1', '--cflags', '--libs'], 44 | uselib_store='NDN_CXX', pkg_config_path=pkg_config_path) 45 | 46 | conf.check_boost() 47 | if conf.env.BOOST_VERSION_NUMBER < 107100: 48 | conf.fatal('The minimum supported version of Boost is 1.71.0.\n' 49 | 'Please upgrade your distribution or manually install a newer version of Boost.\n' 50 | 'For more information, see https://redmine.named-data.net/projects/nfd/wiki/Boost') 51 | 52 | if conf.env.WITH_TESTS: 53 | conf.check_boost(lib='unit_test_framework', mt=True, uselib_store='BOOST_TESTS') 54 | 55 | if conf.env.WITH_TOOLS: 56 | conf.check_boost(lib='program_options', mt=True, uselib_store='BOOST_TOOLS') 57 | 58 | conf.check_compiler_flags() 59 | 60 | # Loading "late" to prevent tests from being compiled with profiling flags 61 | conf.load('coverage') 62 | conf.load('sanitizers') 63 | 64 | # If there happens to be a static library, waf will put the corresponding -L flags 65 | # before dynamic library flags. This can result in compilation failure when the 66 | # system has a different version of the ndn-nac library installed. 67 | conf.env.prepend_value('STLIBPATH', ['.']) 68 | 69 | conf.define_cond('WITH_TESTS', conf.env.WITH_TESTS) 70 | # The config header will contain all defines that were added using conf.define() 71 | # or conf.define_cond(). Everything that was added directly to conf.env.DEFINES 72 | # will not appear in the config header, but will instead be passed directly to the 73 | # compiler on the command line. 74 | conf.write_config_header('src/detail/config.hpp', define_prefix='NAC_') 75 | 76 | def build(bld): 77 | version(bld) 78 | 79 | bld(features='subst', 80 | name='version.hpp', 81 | source='src/version.hpp.in', 82 | target='src/version.hpp', 83 | install_path='${INCLUDEDIR}/ndn-nac', 84 | VERSION_STRING=VERSION_BASE, 85 | VERSION_BUILD=VERSION, 86 | VERSION=int(VERSION_SPLIT[0]) * 1000000 + 87 | int(VERSION_SPLIT[1]) * 1000 + 88 | int(VERSION_SPLIT[2]), 89 | VERSION_MAJOR=VERSION_SPLIT[0], 90 | VERSION_MINOR=VERSION_SPLIT[1], 91 | VERSION_PATCH=VERSION_SPLIT[2]) 92 | 93 | bld.shlib( 94 | target='ndn-nac', 95 | name='libndn-nac', 96 | vnum=VERSION_BASE, 97 | cnum=VERSION_BASE, 98 | source=bld.path.ant_glob('src/**/*.cpp'), 99 | use='BOOST NDN_CXX', 100 | includes='src', 101 | export_includes='src') 102 | 103 | if bld.env.WITH_TESTS: 104 | bld.recurse('tests') 105 | 106 | if bld.env.WITH_TOOLS: 107 | bld.recurse('tools') 108 | 109 | if bld.env.WITH_EXAMPLES: 110 | bld.recurse('examples') 111 | 112 | # Install header files 113 | bld.install_files('${INCLUDEDIR}/ndn-nac', bld.path.find_dir('src').ant_glob('*.hpp')) 114 | bld.install_files('${INCLUDEDIR}/ndn-nac/detail', 'src/detail/config.hpp') 115 | 116 | bld(features='subst', 117 | source='libndn-nac.pc.in', 118 | target='libndn-nac.pc', 119 | install_path='${LIBDIR}/pkgconfig', 120 | VERSION=VERSION_BASE) 121 | 122 | def docs(bld): 123 | from waflib import Options 124 | Options.commands = ['doxygen', 'sphinx'] + Options.commands 125 | 126 | def doxygen(bld): 127 | version(bld) 128 | 129 | if not bld.env.DOXYGEN: 130 | bld.fatal('Cannot build documentation ("doxygen" not found in PATH)') 131 | 132 | bld(features='subst', 133 | name='doxygen.conf', 134 | source=['docs/doxygen.conf.in', 135 | 'docs/named_data_theme/named_data_footer-with-analytics.html.in'], 136 | target=['docs/doxygen.conf', 137 | 'docs/named_data_theme/named_data_footer-with-analytics.html'], 138 | VERSION=VERSION, 139 | HAVE_DOT='YES' if bld.env.DOT else 'NO', 140 | HTML_FOOTER='../build/docs/named_data_theme/named_data_footer-with-analytics.html' \ 141 | if os.getenv('GOOGLE_ANALYTICS', None) \ 142 | else '../docs/named_data_theme/named_data_footer.html', 143 | GOOGLE_ANALYTICS=os.getenv('GOOGLE_ANALYTICS', '')) 144 | 145 | bld(features='doxygen', 146 | doxyfile='docs/doxygen.conf', 147 | use='doxygen.conf') 148 | 149 | def sphinx(bld): 150 | version(bld) 151 | 152 | if not bld.env.SPHINX_BUILD: 153 | bld.fatal('Cannot build documentation ("sphinx-build" not found in PATH)') 154 | 155 | bld(features='sphinx', 156 | config='docs/conf.py', 157 | outdir='docs', 158 | source=bld.path.ant_glob('docs/**/*.rst'), 159 | version=VERSION_BASE, 160 | release=VERSION) 161 | 162 | def version(ctx): 163 | # don't execute more than once 164 | if getattr(Context.g_module, 'VERSION_BASE', None): 165 | return 166 | 167 | Context.g_module.VERSION_BASE = Context.g_module.VERSION 168 | Context.g_module.VERSION_SPLIT = VERSION_BASE.split('.') 169 | 170 | # first, try to get a version string from git 171 | version_from_git = '' 172 | try: 173 | cmd = ['git', 'describe', '--abbrev=8', '--always', '--match', f'{GIT_TAG_PREFIX}*'] 174 | version_from_git = subprocess.run(cmd, capture_output=True, check=True, text=True).stdout.strip() 175 | if version_from_git: 176 | if GIT_TAG_PREFIX and version_from_git.startswith(GIT_TAG_PREFIX): 177 | Context.g_module.VERSION = version_from_git[len(GIT_TAG_PREFIX):] 178 | elif not GIT_TAG_PREFIX and ('.' in version_from_git or '-' in version_from_git): 179 | Context.g_module.VERSION = version_from_git 180 | else: 181 | # no tags matched (or we are in a shallow clone) 182 | Context.g_module.VERSION = f'{VERSION_BASE}+git.{version_from_git}' 183 | except (OSError, subprocess.SubprocessError): 184 | pass 185 | 186 | # fallback to the VERSION.info file, if it exists and is not empty 187 | version_from_file = '' 188 | version_file = ctx.path.find_node('VERSION.info') 189 | if version_file is not None: 190 | try: 191 | version_from_file = version_file.read().strip() 192 | except OSError as e: 193 | Logs.warn(f'{e.filename} exists but is not readable ({e.strerror})') 194 | if version_from_file and not version_from_git: 195 | Context.g_module.VERSION = version_from_file 196 | return 197 | 198 | # update VERSION.info if necessary 199 | if version_from_file == Context.g_module.VERSION: 200 | # already up-to-date 201 | return 202 | if version_file is None: 203 | version_file = ctx.path.make_node('VERSION.info') 204 | try: 205 | version_file.write(Context.g_module.VERSION) 206 | except OSError as e: 207 | Logs.warn(f'{e.filename} is not writable ({e.strerror})') 208 | 209 | def dist(ctx): 210 | ctx.algo = 'tar.xz' 211 | version(ctx) 212 | 213 | def distcheck(ctx): 214 | ctx.algo = 'tar.xz' 215 | version(ctx) 216 | --------------------------------------------------------------------------------