├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github └── workflows │ ├── codeql-analysis.yml │ └── test.yml ├── .gitignore ├── .readthedocs.yml ├── AUTHORS.rst ├── CHANGELOG.rst ├── COPYING ├── Makefile ├── README.rst ├── docs ├── Makefile ├── _static │ ├── lvs-details-opt1.svg │ ├── lvs-ptree-signing.svg │ ├── lvs-ptree.svg │ ├── schema-example1-policy.svg │ └── schema-example1-schema.svg ├── _templates │ └── .gitkeep ├── conf.py ├── index.rst ├── make.bat └── src │ ├── app.rst │ ├── appv2.rst │ ├── authors.rst │ ├── changelog.rst │ ├── contribute_support.rst │ ├── encoding │ ├── encoding.rst │ ├── name.rst │ ├── ndn_format_0_3.rst │ ├── tlv_model.rst │ └── tlv_var.rst │ ├── examples │ ├── basic_app.rst │ ├── examples.rst │ └── tlv_model.rst │ ├── future.rst │ ├── installation.rst │ ├── lvs │ ├── binary-format.rst │ ├── demonstration.rst │ ├── details.rst │ ├── lvs.rst │ └── package.rst │ ├── misc.rst │ ├── readme.rst │ ├── schema │ ├── custom_node.rst │ ├── ex1.rst │ ├── policies.rst │ ├── schema.rst │ ├── schema_tree.rst │ └── utils.rst │ └── security │ └── security.rst ├── examples ├── appv2 │ ├── basic_packets │ │ ├── consumer.py │ │ └── producer.py │ ├── forwarding_hint │ │ ├── consumer.py │ │ └── producer.py │ ├── keychain_cert │ │ ├── fetch_certificate.py │ │ └── keychain_register.py │ └── svs │ │ └── sync_example.py ├── catchunks.py ├── consumer.py ├── dpdk_experimental │ ├── udp_consumer.py │ └── udp_producer.py ├── lvs │ ├── consumer.py │ ├── ls-certificates.sh │ ├── pib.db │ ├── privKeys │ │ ├── 97cc0c81777e988284f8030c4dfaef1f828daf64971ef1bcaa8a4ca3b1f50cbf.privkey │ │ ├── a3e2e001a71e797847dab7f406fd450b4ceb18ab3261a8cf2c498958bf35f17f.privkey │ │ └── af3b726c4c32c9de141e6a35865bbbc3a8b2b12d85e27910d8f908e05c47d201.privkey │ └── producer.py ├── nfd_status.py ├── producer.py ├── putchunks.py ├── rdrnode.py ├── rpc_consumer.py └── rpc_producer.py ├── poetry.lock ├── pyproject.toml ├── src └── ndn │ ├── __init__.py │ ├── app.py │ ├── app_support │ ├── __init__.py │ ├── dispatcher.py │ ├── ecies.py │ ├── keychain_register.py │ ├── light_versec │ │ ├── __init__.py │ │ ├── binary.py │ │ ├── checker.py │ │ ├── compiler.py │ │ ├── grammar.py │ │ ├── parser.py │ │ └── validator.py │ ├── nfd_mgmt.py │ ├── security_v2.py │ ├── segment_fetcher.py │ └── svs │ │ ├── __init__.py │ │ ├── sync.py │ │ └── tlv.py │ ├── appv2.py │ ├── bin │ ├── __init__.py │ ├── nfdc │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── cmd_get_face.py │ │ ├── cmd_get_route.py │ │ ├── cmd_get_status.py │ │ ├── cmd_get_strategy.py │ │ ├── cmd_new_face.py │ │ ├── cmd_new_route.py │ │ ├── cmd_remove_face.py │ │ ├── cmd_remove_route.py │ │ ├── cmd_remove_strategy.py │ │ ├── cmd_set_strategy.py │ │ └── utils.py │ ├── sec │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── cmd_export_cert.py │ │ ├── cmd_get_childitem.py │ │ ├── cmd_get_default.py │ │ ├── cmd_get_signreq.py │ │ ├── cmd_import_cert.py │ │ ├── cmd_init_pib.py │ │ ├── cmd_new_item.py │ │ ├── cmd_remove_item.py │ │ ├── cmd_set_default.py │ │ ├── cmd_sign_cert.py │ │ └── utils.py │ └── tools │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── cmd_compile_lvs.py │ │ ├── cmd_fetch_data.py │ │ ├── cmd_fetch_rdrcontent.py │ │ ├── cmd_serve_data.py │ │ └── cmd_serve_rdrcontent.py │ ├── client_conf.py │ ├── contrib │ ├── __init__.py │ ├── boost_info │ │ ├── __init__.py │ │ └── parser.py │ └── cocoapy │ │ ├── LICENSE │ │ ├── __init__.py │ │ ├── cocoalibs.py │ │ ├── cocoatypes.py │ │ └── runtime.py │ ├── encoding │ ├── __init__.py │ ├── name │ │ ├── Component.py │ │ ├── Name.py │ │ └── __init__.py │ ├── ndn_format_0_3.py │ ├── ndn_format_0_3_2017.py │ ├── ndnlp_v2.py │ ├── signer.py │ ├── tlv_model.py │ ├── tlv_type.py │ └── tlv_var.py │ ├── name_tree.py │ ├── platform │ ├── __init__.py │ ├── general.py │ ├── linux.py │ ├── osx.py │ └── windows.py │ ├── schema │ ├── __init__.py │ ├── policy.py │ ├── schema_tree.py │ ├── simple_cache.py │ ├── simple_node.py │ ├── simple_trust.py │ └── util.py │ ├── security │ ├── __init__.py │ ├── keychain │ │ ├── __init__.py │ │ ├── keychain.py │ │ ├── keychain_digest.py │ │ └── keychain_sqlite3.py │ ├── signer │ │ ├── __init__.py │ │ ├── ed25519_signer.py │ │ ├── null_signer.py │ │ ├── sha256_digest_signer.py │ │ ├── sha256_ecdsa_signer.py │ │ ├── sha256_hmac_signer.py │ │ └── sha256_rsa_signer.py │ ├── tpm │ │ ├── __init__.py │ │ ├── tpm.py │ │ ├── tpm_cng.py │ │ ├── tpm_file.py │ │ └── tpm_osx_keychain.py │ └── validator │ │ ├── __init__.py │ │ ├── cascade_validator.py │ │ ├── digest_validator.py │ │ └── known_key_validator.py │ ├── transport │ ├── __init__.py │ ├── dummy_face.py │ ├── face.py │ ├── ip_face.py │ ├── ndn_dpdk.py │ ├── nfd_registerer.py │ ├── prefix_registerer.py │ ├── stream_face.py │ └── udp_face.py │ ├── types.py │ └── utils.py └── tests ├── __init__.py ├── ecies_test.py ├── encoding ├── name_test.py ├── ndn_format_0_3_test.py ├── ndnlp_v2_test.py ├── tlv_model_test.py └── tlv_var_test.py ├── face_test.py ├── integration ├── app_test.py ├── app_v2_test.py └── keychain_test.py ├── misc ├── boost_info_test.py └── light_versec_test.py └── security └── signer_test.py /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/zjkmxy/yanfd-docker:22.12 2 | 3 | # [Optional] If your pip requirements rarely change, uncomment this section to add them to the image. 4 | # COPY requirements.txt /tmp/pip-tmp/ 5 | # RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \ 6 | # && rm -rf /tmp/pip-tmp 7 | 8 | # [Optional] Uncomment this section to install additional OS packages. 9 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 10 | # && apt-get -y install --no-install-recommends 11 | 12 | # [Optional] Uncomment this line to install global node packages. 13 | # RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 14 | 15 | RUN pip3 --disable-pip-version-check --no-cache-dir install rstcheck sphinx-rtd-theme sphinx-autodoc-typehints \ 16 | readthedocs-sphinx-ext && rm -rf /tmp/pip-tmp 17 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "python-ndn devcontainer", 3 | "build": { 4 | "dockerfile": "./Dockerfile", 5 | "context": "." 6 | }, 7 | "features": { 8 | "ghcr.io/devcontainers/features/common-utils:2": { 9 | "installZsh": "true", 10 | "username": "vscode", 11 | "userUid": "1000", 12 | "userGid": "1000", 13 | "upgradePackages": "true" 14 | }, 15 | "ghcr.io/devcontainers/features/python:1": { 16 | "version": "3.11" 17 | }, 18 | "ghcr.io/devcontainers/features/node:1": {}, 19 | "ghcr.io/devcontainers/features/git:1": { 20 | "version": "latest", 21 | "ppa": "false" 22 | } 23 | }, 24 | "customizations": { 25 | "vscode": { 26 | "settings": { 27 | "terminal.integrated.shellIntegration.enabled": true, 28 | "terminal.integrated.profiles.linux": { 29 | "zsh": { 30 | "path": "zsh", 31 | "args": [] 32 | } 33 | }, 34 | "terminal.integrated.defaultProfile.linux": "zsh", 35 | "python.defaultInterpreterPath": "/usr/local/bin/python", 36 | "python.linting.enabled": true, 37 | "python.linting.pylintEnabled": true, 38 | "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", 39 | "python.formatting.blackPath": "/usr/local/py-utils/bin/black", 40 | "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", 41 | "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", 42 | "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", 43 | "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", 44 | "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", 45 | "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", 46 | "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint", 47 | "esbonio.sphinx.confDir": "${workspaceFolder}/docs", 48 | "esbonio.sphinx.buildDir": "${workspaceFolder}/docs/_build/html", 49 | "esbonio.sphinx.srcDir": "${workspaceFolder}/docs", 50 | "python.testing.pytestArgs": [ 51 | "tests" 52 | ], 53 | "python.testing.unittestEnabled": false, 54 | "python.testing.nosetestsEnabled": false, 55 | "python.testing.pytestEnabled": true 56 | } 57 | }, 58 | "extensions": [ 59 | "ms-python.python", 60 | "ms-python.vscode-pylance", 61 | "lextudio.restructuredtext" 62 | ] 63 | }, 64 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 65 | // "forwardPorts": [], 66 | // Use 'postCreateCommand' to run commands after the container is created. 67 | "postCreateCommand": "pip3 install --force-reinstall -e .[dev,docs]", 68 | // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. 69 | "remoteUser": "vscode" 70 | } -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: CodeQL 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | schedule: 9 | - cron: '33 13 * * 2' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | language: [ 'python' ] 24 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 25 | # Learn more: 26 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v4 31 | 32 | # Initializes the CodeQL tools for scanning. 33 | - name: Initialize CodeQL 34 | uses: github/codeql-action/init@v3 35 | with: 36 | languages: ${{ matrix.language }} 37 | # If you wish to specify custom queries, you can do so here or in a config file. 38 | # By default, queries listed here will override any specified in a config file. 39 | # Prefix the list here with "+" to use these queries and those in the config file. 40 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 41 | 42 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 43 | # If this step fails, then you should remove it and run the build manually (see below) 44 | - name: Autobuild 45 | uses: github/codeql-action/autobuild@v3 46 | 47 | # ℹ️ Command-line programs to run using the OS shell. 48 | # 📚 https://git.io/JvXDl 49 | 50 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 51 | # and modify them (or add more) to build your code if your project 52 | # uses a compiled language 53 | 54 | #- run: | 55 | # make bootstrap 56 | # make release 57 | 58 | - name: Perform CodeQL Analysis 59 | uses: github/codeql-action/analyze@v3 60 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | python-version: ['3.13', 'pypy-3.10'] 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | - name: Set up Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | - name: Upgrade pip 27 | run: | 28 | python -m pip install --upgrade pip 29 | - name: Install dependencies 30 | run: | 31 | pip install ".[dev]" 32 | - name: Lint with flake8 33 | run: | 34 | # stop the build if there are Python syntax errors or undefined names 35 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 36 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 37 | flake8 src --exclude=src/ndn/contrib --count --ignore=F403,F405,W503,E226 \ 38 | --exit-zero --max-complexity=20 --max-line-length=120 --statistics 39 | flake8 tests --count --ignore=F403,F405,W503,E226,E222,W504 \ 40 | --exit-zero --max-complexity=50 --max-line-length=120 --statistics 41 | - name: Test with pytest 42 | run: | 43 | pytest tests 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | t Byte-compiled / optimized / DLL files 2 | deps.txt 3 | archive 4 | saver 5 | *~ 6 | styles 7 | pngs 8 | preds 9 | 10 | *.sw* 11 | data 12 | __pycache__/ 13 | *.py[cod] 14 | *$py.class 15 | 16 | # C extensions 17 | *.so 18 | 19 | # Distribution / packaging 20 | .Python 21 | env/ 22 | build/ 23 | develop-eggs/ 24 | dist/ 25 | downloads/ 26 | eggs/ 27 | .eggs/ 28 | lib/ 29 | lib64/ 30 | parts/ 31 | sdist/ 32 | var/ 33 | *.egg-info/ 34 | .installed.cfg 35 | *.egg 36 | 37 | # PyInstaller 38 | # Usually these files are written by a python script from a template 39 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 40 | *.manifest 41 | *.spec 42 | 43 | # Installer logs 44 | pip-log.txt 45 | pip-delete-this-directory.txt 46 | 47 | # Unit test / coverage reports 48 | htmlcov/ 49 | .tox/ 50 | .coverage 51 | .coverage.* 52 | .cache 53 | nosetests.xml 54 | coverage.xml 55 | *,cover 56 | .hypothesis/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # IPython Notebook 80 | .ipynb_checkpoints 81 | 82 | # pyenv 83 | .python-version 84 | 85 | # celery beat schedule file 86 | celerybeat-schedule 87 | 88 | # dotenv 89 | .env 90 | 91 | # virtualenv 92 | venv/ 93 | ENV/ 94 | pypyvenv/ 95 | 96 | # Spyder project settings 97 | .spyderproject 98 | 99 | # Rope project settings 100 | .ropeproject 101 | 102 | # PyCharm 103 | .idea 104 | 105 | # checkpoint 106 | checkpoint 107 | 108 | ############# 109 | 110 | # VS Code 111 | .vscode 112 | 113 | # Mac 114 | *.DS_Store 115 | 116 | # Python 117 | *.pyc 118 | 119 | # pytest 120 | .pytest_cache 121 | 122 | # Special 123 | # Requirements should be specified by setup.py 124 | main.py 125 | 126 | temp/ 127 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Build documentation in the docs/ directory with Sphinx 9 | sphinx: 10 | configuration: docs/conf.py 11 | 12 | # Build documentation with MkDocs 13 | #mkdocs: 14 | # configuration: mkdocs.yml 15 | 16 | # Optionally build your docs in additional formats such as PDF and ePub 17 | formats: all 18 | 19 | build: 20 | os: ubuntu-22.04 21 | tools: 22 | python: "3.11" 23 | 24 | # Optionally set the version of Python and requirements required to build your docs 25 | python: 26 | install: 27 | - method: pip 28 | path: . 29 | extra_requirements: 30 | - docs 31 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | Authors 2 | ======= 3 | 4 | * Xinyu Ma 5 | * Zhaoning Kong 6 | * Eric Newberry 7 | * Junxiao Shi 8 | * Tianyuan Yu 9 | * Alwin Kahlert 10 | * Dylan Hensley 11 | 12 | Anonymous Contributors 13 | ====================== 14 | 15 | * GlassyYang 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | pytest tests 3 | 4 | test-cov: 5 | pytest tests --cov=src --cov-report term-missing 6 | 7 | upload: 8 | rm -rf dist 9 | poetry build 10 | poetry publish 11 | 12 | lint: 13 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 14 | flake8 src --exclude=src/ndn/contrib --count --ignore=F403,F405,W503,E226 \ 15 | --exit-zero --max-complexity=20 --max-line-length=120 --statistics 16 | flake8 tests --count --ignore=F403,F405,W503,E226,E222,W504 \ 17 | --exit-zero --max-complexity=50 --max-line-length=120 --statistics 18 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | python-ndn 2 | ========== 3 | 4 | |Test Badge| 5 | |Code Size| 6 | |Release Badge| 7 | |Doc Badge| 8 | 9 | A Named Data Networking client library with AsyncIO support in Python 3. 10 | 11 | It supports Python >=3.10 and PyPy3.10 >=7.3.12. 12 | 13 | Please see our documentation_ if you have any issues. 14 | 15 | .. |Test Badge| image:: https://github.com/named-data/python-ndn/actions/workflows/test.yml/badge.svg 16 | :target: https://github.com/named-data/python-ndn 17 | :alt: Test Status 18 | 19 | .. |Code Size| image:: https://img.shields.io/github/languages/code-size/named-data/python-ndn 20 | :target: https://github.com/named-data/python-ndn 21 | :alt: Code Size 22 | 23 | .. |Release Badge| image:: https://img.shields.io/pypi/v/python-ndn?label=release 24 | :target: https://pypi.org/project/python-ndn/ 25 | :alt: Release Ver 26 | 27 | .. |Doc Badge| image:: https://readthedocs.org/projects/python-ndn/badge/?version=latest 28 | :target: https://python-ndn.readthedocs.io/en/latest/?badge=latest 29 | :alt: Doc Status 30 | 31 | .. _documentation: https://python-ndn.readthedocs.io/en/latest 32 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/python-ndn/440f65d092a544f5e5571cc71b7f4861c702a3f7/docs/_templates/.gitkeep -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | sys.path.insert(0, os.path.abspath('../src')) 16 | 17 | from ndn import __version__ 18 | 19 | # -- Project information ----------------------------------------------------- 20 | 21 | project = 'python-ndn' 22 | copyright = '2019-2023, The python-ndn authors' 23 | author = 'Xinyu Ma' 24 | 25 | # The full version, including alpha/beta/rc tags 26 | release = __version__ 27 | 28 | 29 | # -- General configuration --------------------------------------------------- 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = [ 35 | 'sphinx.ext.autodoc', 36 | "sphinx_rtd_theme", 37 | 'sphinx_autodoc_typehints', 38 | ] 39 | 40 | # Add any paths that contain templates here, relative to this directory. 41 | templates_path = ['_templates'] 42 | 43 | # List of patterns, relative to source directory, that match files and 44 | # directories to ignore when looking for source files. 45 | # This pattern also affects html_static_path and html_extra_path. 46 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 47 | 48 | # The master toctree document. 49 | master_doc = 'index' 50 | 51 | 52 | # -- Options for HTML output ------------------------------------------------- 53 | 54 | # The theme to use for HTML and HTML Help pages. See the documentation for 55 | # a list of builtin themes. 56 | # 57 | html_theme = "sphinx_rtd_theme" 58 | 59 | # The theme to use for HTML and HTML Help pages. See the documentation for 60 | # a list of builtin themes. 61 | pygments_style = 'sphinx' 62 | 63 | # Add any paths that contain custom static files (such as style sheets) here, 64 | # relative to this directory. They are copied after the builtin static files, 65 | # so a file named "default.css" will overwrite the builtin "default.css". 66 | html_static_path = ['_static'] 67 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. python-ndn documentation master file, created by 2 | sphinx-quickstart on Mon Oct 7 20:35:12 2019. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to python-ndn's documentation! 7 | ====================================== 8 | 9 | 10 | Table Of Contents 11 | ----------------- 12 | .. toctree:: 13 | :maxdepth: 3 14 | 15 | src/readme 16 | src/installation 17 | src/app 18 | src/appv2 19 | src/encoding/encoding 20 | src/security/security 21 | src/schema/schema 22 | src/lvs/lvs 23 | src/misc 24 | src/examples/examples 25 | src/contribute_support 26 | src/future 27 | src/authors 28 | src/changelog 29 | 30 | 31 | Indices and tables 32 | ------------------ 33 | 34 | * :ref:`genindex` 35 | * :ref:`modindex` 36 | * :ref:`search` 37 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/src/appv2.rst: -------------------------------------------------------------------------------- 1 | :mod:`ndn.appv2` package 2 | ======================== 3 | 4 | Introduction 5 | ------------ 6 | 7 | The :mod:`ndn.appv2` package contains ``NDNApp`` class. 8 | It offers the functionalities similar to an application face in other NDN libraries. 9 | Main features include: 10 | 11 | + Establish a connection to NDN forwarder. 12 | + Consumer: express Interests and receive the reply Data. 13 | + Producer: attach Interest handler function to a name prefix, to handle incoming Interests. 14 | + Producer: register and unregister prefixes in the forwarder. 15 | 16 | This package is a rewrite from :mod:`ndn.app` package. 17 | Major differences from that package are: 18 | 19 | + Initial support for PIT token. 20 | + Send signed Interests for NFD management commands. 21 | 22 | Reference 23 | --------- 24 | 25 | .. automodule:: ndn.appv2 26 | :members: 27 | -------------------------------------------------------------------------------- /docs/src/authors.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../AUTHORS.rst 2 | -------------------------------------------------------------------------------- /docs/src/changelog.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../CHANGELOG.rst 2 | -------------------------------------------------------------------------------- /docs/src/contribute_support.rst: -------------------------------------------------------------------------------- 1 | Contribute and Support 2 | ====================== 3 | 4 | - Please submit any changes via `GitHub`_ pull requests. 5 | - If you use any existing code, please make sure it is compatible to *Apache License Version 2.0* 6 | - Ensure that your code complies to `PEP8`_. 7 | - Ensure that your code works with *Python 3.10* and *PyPy3.10 7.3.12*. 8 | - Please contact the author first if your changes are not back compatible or import new dependencies. 9 | - Write unit tests. There is no need to cover all code, but please cover as much as you can. 10 | - Add your name to ``AUTHORS.rst``. 11 | 12 | .. _GitHub: https://github.com/named-data/python-ndn 13 | .. _PEP8: https://www.python.org/dev/peps/pep-0008/ 14 | -------------------------------------------------------------------------------- /docs/src/encoding/encoding.rst: -------------------------------------------------------------------------------- 1 | :mod:`ndn.encoding` package 2 | ============================ 3 | 4 | Introduction 5 | ------------ 6 | 7 | The :mod:`ndn.encoding` package contains classes and functions 8 | that help to encode and decode NDN Name, NameComponent, Data and Interest. 9 | 10 | There are three parts of this package: 11 | 12 | 1. **TLV elements**: process TLV variables, Names and NameComponents. 13 | 14 | 2. **TlvModel**: design a general way to describe a TLV format. 15 | A TLV object can be described with a class derived from :any:`TlvModel`, 16 | with members of type :any:`Field`. 17 | 18 | 3. **NDN Packet Fotmat v0.3**: functions used to encode and parse 19 | Interest and Data packets in 20 | `NDN Packet Format Spec 0.3 `_. 21 | 22 | .. _label-different-names: 23 | 24 | :any:`FormalName` and :any:`NonStrictName` 25 | ------------------------------------------ 26 | 27 | To increase the flexibility, API in ``python-ndn`` accepts Name arguments in a wide range of formats, 28 | i.e. :any:`NonStrictName`, but returns an unified form, :any:`FormalName`. 29 | 30 | A Component is a NameComponent encoded in TLV format. 31 | 32 | .. code-block:: python3 33 | 34 | component = b'\x08\x09component' 35 | 36 | A :any:`FormalName` is a list of encoded Components. 37 | 38 | .. code-block:: python3 39 | 40 | formal_name = [bytearray(b'\x08\x06formal'), b'\x08\x04name'] 41 | 42 | A :any:`NonStrictName` is any of below: 43 | 44 | - A URI string. 45 | 46 | .. code-block:: python3 47 | 48 | casual_name_1 = "/non-strict/8=name" 49 | 50 | - A list or iterator of Components, in the form of either encoded TLV or URI string. 51 | 52 | .. code-block:: python3 53 | 54 | casual_name_2 = [bytearray(b'\x08\x0anon-strict'), 'name'] 55 | casual_name_3 = (f'{x}' for x in range(3)) 56 | 57 | - An encoded Name of type :class:`bytes`, :class:`bytearray` or :class:`memoryview`. 58 | 59 | .. code-block:: python3 60 | 61 | casual_name_4 = b'\x07\x12\x08\x0anon-strict\x08\x04name' 62 | 63 | Customized TLV Models 64 | --------------------- 65 | 66 | See :doc:`../examples/tlv_model` 67 | 68 | Reference 69 | --------- 70 | 71 | .. toctree:: 72 | 73 | TLV Variables 74 | Name and Component 75 | TLV Model 76 | NDN Packet Format 0.3 77 | -------------------------------------------------------------------------------- /docs/src/encoding/name.rst: -------------------------------------------------------------------------------- 1 | Name and Component 2 | ================== 3 | 4 | ``ndn.encoding.name.Component`` module 5 | -------------------------------------- 6 | 7 | .. automodule:: ndn.encoding.name.Component 8 | :members: 9 | 10 | ``ndn.encoding.name.Name`` module 11 | --------------------------------- 12 | 13 | .. automodule:: ndn.encoding.name.Name 14 | :members: 15 | -------------------------------------------------------------------------------- /docs/src/encoding/ndn_format_0_3.rst: -------------------------------------------------------------------------------- 1 | NDN Packet Format 0.3 2 | ===================== 3 | 4 | .. automodule:: ndn.encoding.ndn_format_0_3 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/src/encoding/tlv_model.rst: -------------------------------------------------------------------------------- 1 | TLV Model 2 | ========= 3 | 4 | .. automodule:: ndn.encoding.tlv_model 5 | 6 | .. autoexception:: DecodeError 7 | :members: 8 | 9 | .. autoexception:: IncludeBaseError 10 | :members: 11 | 12 | .. autoclass:: IncludeBase 13 | :members: 14 | 15 | .. autoclass:: Field 16 | :members: __get__, __set__, encode_into, encoded_length, get_value, parse_from, skipping_process 17 | 18 | .. autoclass:: ProcedureArgument 19 | :members: __get__, __set__, get_arg, set_arg 20 | :exclude-members: encoded_length, encoded_into, parse_from 21 | 22 | .. autoclass:: OffsetMarker 23 | :exclude-members: encoded_length, encoded_into, parse_from, skipping_process 24 | 25 | .. autoclass:: UintField 26 | :exclude-members: encoded_length, encoded_into, parse_from 27 | 28 | .. autoclass:: BoolField 29 | :exclude-members: encoded_length, encoded_into, parse_from 30 | 31 | .. autoclass:: NameField 32 | :exclude-members: encoded_length, encoded_into, parse_from 33 | 34 | .. autoclass:: BytesField 35 | :exclude-members: encoded_length, encoded_into, parse_from 36 | 37 | .. autoclass:: ModelField 38 | :exclude-members: encoded_length, encoded_into, parse_from 39 | 40 | .. autoclass:: RepeatedField 41 | :exclude-members: encoded_length, encoded_into, parse_from 42 | 43 | .. autoclass:: TlvModelMeta 44 | :members: 45 | 46 | .. autoclass:: TlvModel 47 | :members: __eq__, asdict, encode, encoded_length, parse 48 | -------------------------------------------------------------------------------- /docs/src/encoding/tlv_var.rst: -------------------------------------------------------------------------------- 1 | TLV Variables 2 | ============= 3 | 4 | .. automodule:: ndn.encoding.tlv_type 5 | :members: 6 | 7 | .. automodule:: ndn.encoding.tlv_var 8 | :members: 9 | -------------------------------------------------------------------------------- /docs/src/examples/basic_app.rst: -------------------------------------------------------------------------------- 1 | Basic Applications 2 | ================== 3 | 4 | Connect to NFD 5 | ~~~~~~~~~~~~~~ 6 | 7 | NDNApp connects to an NFD node and provides interface to express and process Interests. 8 | The following code initializes an NDNApp instance with default configuration. 9 | 10 | .. code-block:: python3 11 | 12 | from ndn.app import NDNApp 13 | app = NDNApp() 14 | app.run_forever() 15 | 16 | If there is a main function for the application, use the ``after_start`` argument. 17 | 18 | .. code-block:: python3 19 | 20 | from ndn.app import NDNApp 21 | app = NDNApp() 22 | 23 | async def main(): 24 | # Do something 25 | app.shutdown() # Close the connection and shutdown 26 | 27 | app.run_forever(after_start=main()) 28 | 29 | Consumer 30 | ~~~~~~~~ 31 | 32 | A consumer can use ``express_interest`` to express an Interest. 33 | If a Data is received and validated, it returns the Name, MetaInfo and Content of Data. 34 | Otherwise, an exception is thrown. 35 | 36 | .. code-block:: python3 37 | 38 | from ndn.encoding import Name 39 | 40 | async def main(): 41 | try: 42 | data_name, meta_info, content = await app.express_interest( 43 | # Interest Name 44 | '/example/testApp/randomData', 45 | must_be_fresh=True, 46 | can_be_prefix=False, 47 | # Interest lifetime in ms 48 | lifetime=6000) 49 | # Print out Data Name, MetaInfo and its conetnt. 50 | print(f'Received Data Name: {Name.to_str(data_name)}') 51 | print(meta_info) 52 | print(bytes(content) if content else None) 53 | except InterestNack as e: 54 | # A NACK is received 55 | print(f'Nacked with reason={e.reason}') 56 | except InterestTimeout: 57 | # Interest times out 58 | print(f'Timeout') 59 | except InterestCanceled: 60 | # Connection to NFD is broken 61 | print(f'Canceled') 62 | except ValidationFailure: 63 | # Validation failure 64 | print(f'Data failed to validate') 65 | finally: 66 | app.shutdown() 67 | 68 | Producer 69 | ~~~~~~~~ 70 | 71 | A producer can call ``route`` to register a permanent route. 72 | Route registration can be done before application is started. 73 | NDNApp will automatically announce that route to the NFD node. 74 | 75 | .. code-block:: python3 76 | 77 | @app.route('/example/testApp') 78 | def on_interest(name, interest_param, application_param): 79 | app.put_data(name, content=b'content', freshness_period=10000) 80 | 81 | -------------------------------------------------------------------------------- /docs/src/examples/examples.rst: -------------------------------------------------------------------------------- 1 | Examples 2 | ======== 3 | 4 | Here lists some examples of python-ndn. 5 | 6 | .. toctree:: 7 | 8 | Basic Applications 9 | Customized TLV Models 10 | -------------------------------------------------------------------------------- /docs/src/future.rst: -------------------------------------------------------------------------------- 1 | Future plans 2 | ============ 3 | 4 | Future releases will include: 5 | 6 | - Add more documentation 7 | - Add more tests 8 | - Security tools: 9 | - Schematized trust (including its necessary components 10 | such as validator, certificate storage, etc.) 11 | - Name-based Access Control 12 | - Application supports: 13 | - Sync protocol 14 | - DLedger 15 | -------------------------------------------------------------------------------- /docs/src/installation.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | Install the latest release with pip:: 6 | 7 | $ pip install python-ndn 8 | 9 | Install the latest development version:: 10 | 11 | $ pip install -U git+https://github.com/named-data/python-ndn.git 12 | 13 | Instructions for developer 14 | -------------------------- 15 | 16 | For development, `poetry `_ is recommended. You need poetry-dynamic-versioning plugin:: 17 | 18 | $ poetry self add "poetry-dynamic-versioning[plugin]" 19 | 20 | And to install the development environment:: 21 | 22 | $ poetry install --all-extras 23 | 24 | To setup a traditional python3 virtual environment with editable installation: 25 | 26 | .. code-block:: bash 27 | 28 | python3 -m venv venv 29 | . venv/bin/activate 30 | pip3 install -e ".[dev,docs]" 31 | 32 | Run all tests: 33 | 34 | .. code-block:: bash 35 | 36 | poetry run make test 37 | 38 | Run static analysis: 39 | 40 | .. code-block:: bash 41 | 42 | poetry run make lint 43 | 44 | Generate the documentation: 45 | 46 | .. code-block:: bash 47 | 48 | poetry run make -C docs html 49 | open docs/_build/html/index.html 50 | 51 | VSCode users can also use the development container obtained from the `.devcontainer` folder. 52 | -------------------------------------------------------------------------------- /docs/src/lvs/package.rst: -------------------------------------------------------------------------------- 1 | :mod:`ndn.app_support.light_versec` package 2 | =========================================== 3 | 4 | .. automodule:: ndn.app_support.light_versec 5 | 6 | .. autofunction:: compile_lvs 7 | 8 | .. autoclass:: Checker 9 | :members: 10 | 11 | .. autoclass:: SemanticError 12 | :members: 13 | 14 | .. autoclass:: LvsModelError 15 | :members: 16 | 17 | .. autonewtypedata:: ndn.app_support.light_versec.checker.UserFn -------------------------------------------------------------------------------- /docs/src/misc.rst: -------------------------------------------------------------------------------- 1 | Miscellaneous packages 2 | ====================== 3 | 4 | :mod:`ndn.types` package 5 | ------------------------ 6 | 7 | .. automodule:: ndn.types 8 | :members: 9 | 10 | :mod:`ndn.utils` package 11 | ------------------------ 12 | 13 | .. automodule:: ndn.utils 14 | :members: 15 | -------------------------------------------------------------------------------- /docs/src/readme.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../README.rst 2 | -------------------------------------------------------------------------------- /docs/src/schema/custom_node.rst: -------------------------------------------------------------------------------- 1 | Custom Nodes 2 | ============ 3 | 4 | .. automodule:: ndn.schema.simple_node 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/src/schema/policies.rst: -------------------------------------------------------------------------------- 1 | Policies 2 | ======== 3 | 4 | Policy Types 5 | ~~~~~~~~~~~~ 6 | 7 | .. automodule:: ndn.schema.policy 8 | :members: 9 | 10 | Trust Policies 11 | ~~~~~~~~~~~~~~ 12 | 13 | .. automodule:: ndn.schema.simple_trust 14 | :members: 15 | 16 | Cache Policies 17 | ~~~~~~~~~~~~~~ 18 | 19 | .. automodule:: ndn.schema.simple_cache 20 | :members: 21 | -------------------------------------------------------------------------------- /docs/src/schema/schema.rst: -------------------------------------------------------------------------------- 1 | :mod:`ndn.schema` package 2 | ============================ 3 | 4 | .. warning:: 5 | 6 | Name Tree Schema (NTSchema) is experimental and capricious. 7 | The current implementation is treated as a proof-of-concept demo. 8 | 9 | Introduction 10 | ------------ 11 | 12 | The :mod:`ndn.schema` package provides an implementation of Name Tree Schema, an application framework that 13 | organizes application functionalities by the applciation namespace. 14 | Modularized NDN libraries can be developed based on it, and 15 | application developers can use those libraries as building blocks. 16 | 17 | The core concept of NTSchema is the namespace schema tree. 18 | The schema tree is a tree structure that contains all possible naming conventions of an application. 19 | Different from a tree of names, its edge may be a pattern variable instead of a specific name component. 20 | For example, the path ``//KEY/`` can be used to represents a naming convention of a key, 21 | where specific keys -- like ``/Alice/KEY/%01`` and ``/Bob/KEY/%c2`` match with it. 22 | 23 | Two main components of NTSchema are custom nodes and policies. 24 | In the schema tree, every node represents a namespace. 25 | After matching with a specific name, a node can be used to produce and consume data. 26 | For example, if we call ``matched_node = tree.match('/Alice/KEY/%01')``, it will return a matching of node 27 | ``//KEY/`` with variable setting ``Identity='Alice', KeyID=\x01``. 28 | Then we call ``matched_node.provide(key_data)``, it will generate the key with data ``key_data`` and make it available. 29 | When we call ``key_data = matched_node.need()``, it will try to fetch the key. 30 | A custom node will have customized pipeline to handle ``provide`` and ``need`` function calls. 31 | Policies are annotations attached to nodes, that specifies user-defined policies that are security, storage, etc. 32 | 33 | Examples 34 | -------- 35 | 36 | .. toctree:: 37 | 38 | 1 - File Sharing 39 | 40 | Reference 41 | --------- 42 | 43 | .. toctree:: 44 | 45 | Namespace Schema Tree 46 | Utils 47 | Custom Nodes 48 | Policies 49 | -------------------------------------------------------------------------------- /docs/src/schema/schema_tree.rst: -------------------------------------------------------------------------------- 1 | Namespace Schema Tree 2 | ===================== 3 | 4 | .. automodule:: ndn.schema.schema_tree 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/src/schema/utils.rst: -------------------------------------------------------------------------------- 1 | Utils 2 | ===== 3 | 4 | .. automodule:: ndn.schema.util 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/src/security/security.rst: -------------------------------------------------------------------------------- 1 | :mod:`ndn.security` package 2 | ============================ 3 | 4 | Introduction 5 | ------------ 6 | 7 | The :mod:`ndn.security` package provides basic tools for security use. 8 | 9 | Signer 10 | ------ 11 | 12 | A :any:`Signer` is a class used to sign a packet during encoding. 13 | 14 | .. autoclass:: ndn.encoding.Signer 15 | :members: 16 | 17 | Validator 18 | --------- 19 | 20 | A :any:`Validator` is a async function called to validate an Interest or Data packet. 21 | It takes 2 arguments: a :any:`FormalName` and a :any:`SignaturePtrs`, 22 | and returns whether the packet is validated. 23 | 24 | Keychain 25 | -------- 26 | 27 | A :any:`Keychain` is a class which contains Identities, Keys associated with Identities and associated Certificates. 28 | 29 | .. autoclass:: ndn.security.keychain.Keychain 30 | :members: 31 | 32 | 33 | KeychainDigest 34 | ~~~~~~~~~~~~~~ 35 | .. automodule:: ndn.security.keychain.keychain_digest 36 | :members: 37 | 38 | 39 | KeychainSqlite3 40 | ~~~~~~~~~~~~~~~ 41 | 42 | This is the default Keychain. 43 | 44 | .. automodule:: ndn.security.keychain.keychain_sqlite3 45 | :members: 46 | -------------------------------------------------------------------------------- /examples/appv2/basic_packets/consumer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2022 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import logging 19 | from ndn import utils, appv2, types 20 | from ndn import encoding as enc 21 | 22 | 23 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 24 | datefmt='%Y-%m-%d %H:%M:%S', 25 | level=logging.INFO, 26 | style='{') 27 | 28 | 29 | app = appv2.NDNApp() 30 | 31 | 32 | async def main(): 33 | try: 34 | timestamp = utils.timestamp() 35 | name = enc.Name.from_str('/example/testApp/randomData') + [enc.Component.from_timestamp(timestamp)] 36 | print(f'Sending Interest {enc.Name.to_str(name)}, {enc.InterestParam(must_be_fresh=True, lifetime=6000)}') 37 | # TODO: Write a better validator 38 | data_name, content, pkt_context = await app.express( 39 | name, validator=appv2.pass_all, 40 | must_be_fresh=True, can_be_prefix=False, lifetime=6000) 41 | 42 | print(f'Received Data Name: {enc.Name.to_str(data_name)}') 43 | print(pkt_context['meta_info']) 44 | print(bytes(content) if content else None) 45 | except types.InterestNack as e: 46 | print(f'Nacked with reason={e.reason}') 47 | except types.InterestTimeout: 48 | print(f'Timeout') 49 | except types.InterestCanceled: 50 | print(f'Canceled') 51 | except types.ValidationFailure: 52 | print(f'Data failed to validate') 53 | finally: 54 | app.shutdown() 55 | 56 | 57 | if __name__ == '__main__': 58 | app.run_forever(after_start=main()) 59 | -------------------------------------------------------------------------------- /examples/appv2/basic_packets/producer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2022 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import typing 19 | import logging 20 | from ndn import appv2 21 | from ndn import encoding as enc 22 | 23 | 24 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 25 | datefmt='%Y-%m-%d %H:%M:%S', 26 | level=logging.INFO, 27 | style='{') 28 | 29 | 30 | app = appv2.NDNApp() 31 | keychain = app.default_keychain() 32 | 33 | 34 | @app.route('/example/testApp') 35 | def on_interest(name: enc.FormalName, _app_param: typing.Optional[enc.BinaryStr], 36 | reply: appv2.ReplyFunc, context: appv2.PktContext): 37 | print(f'>> I: {enc.Name.to_str(name)}, {context["int_param"]}') 38 | content = "Hello, world!".encode() 39 | reply(app.make_data(name, content=content, signer=keychain.get_signer({}), 40 | freshness_period=10000)) 41 | print(f'<< D: {enc.Name.to_str(name)}') 42 | print(enc.MetaInfo(freshness_period=10000)) 43 | print(f'Content: (size: {len(content)})') 44 | print('') 45 | 46 | 47 | if __name__ == '__main__': 48 | app.run_forever() 49 | -------------------------------------------------------------------------------- /examples/appv2/forwarding_hint/consumer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2022 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import logging 19 | from ndn import utils, appv2, types 20 | from ndn import encoding as enc 21 | 22 | 23 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 24 | datefmt='%Y-%m-%d %H:%M:%S', 25 | level=logging.INFO, 26 | style='{') 27 | 28 | 29 | app = appv2.NDNApp() 30 | 31 | 32 | async def express_int(name, fw_hint): 33 | try: 34 | if fw_hint is None: 35 | print(f'Sending Interest {enc.Name.to_str(name)}, {enc.InterestParam(must_be_fresh=True, lifetime=6000)}') 36 | data_name, content, pkt_context = await app.express( 37 | name, validator=appv2.pass_all, 38 | must_be_fresh=True, can_be_prefix=False, lifetime=6000) 39 | else: 40 | print(f'Sending Interest {enc.Name.to_str(name)}, ' 41 | f'{enc.InterestParam(must_be_fresh=True, lifetime=6000, forwarding_hint=[fw_hint])}') 42 | data_name, content, pkt_context = await app.express( 43 | name, validator=appv2.pass_all, 44 | must_be_fresh=True, can_be_prefix=False, lifetime=6000, forwarding_hint=[fw_hint]) 45 | 46 | print(f'Received Data Name: {enc.Name.to_str(data_name)}') 47 | print(pkt_context['meta_info']) 48 | print(bytes(content) if content else None) 49 | except types.InterestNack as e: 50 | print(f'Nacked with reason={e.reason}') 51 | except types.InterestTimeout: 52 | print(f'Timeout') 53 | except types.InterestCanceled: 54 | print(f'Canceled') 55 | except types.ValidationFailure: 56 | print(f'Data failed to validate') 57 | 58 | 59 | async def main(): 60 | timestamp = utils.timestamp() 61 | await express_int(enc.Name.from_str('/repo/command/random') + [enc.Component.from_timestamp(timestamp)], 62 | None) 63 | await express_int(enc.Name.from_str('/example/testApp/randomData') + [enc.Component.from_timestamp(timestamp)], 64 | enc.Name.from_str('/repo/files')) 65 | app.shutdown() 66 | 67 | 68 | if __name__ == '__main__': 69 | app.run_forever(after_start=main()) 70 | -------------------------------------------------------------------------------- /examples/appv2/forwarding_hint/producer.py: -------------------------------------------------------------------------------- 1 | import typing 2 | import logging 3 | from ndn import appv2 4 | from ndn import encoding as enc 5 | 6 | 7 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 8 | datefmt='%Y-%m-%d %H:%M:%S', 9 | level=logging.INFO, 10 | style='{') 11 | 12 | 13 | app = appv2.NDNApp() 14 | keychain = app.default_keychain() 15 | 16 | 17 | @app.route('/repo/command') 18 | def on_cmd(name: enc.FormalName, _app_param: typing.Optional[enc.BinaryStr], 19 | reply: appv2.ReplyFunc, context: appv2.PktContext): 20 | print(f'>> I: {enc.Name.to_str(name)}, {context["int_param"]}') 21 | content = "Hello, world!".encode() 22 | reply(app.make_data(name, content=content, signer=keychain.get_signer({}), 23 | freshness_period=10000)) 24 | print(f'<< D: {enc.Name.to_str(name)}') 25 | print(enc.MetaInfo(freshness_period=10000)) 26 | print(f'Content: (size: {len(content)})') 27 | print('') 28 | 29 | 30 | # The following function catches all Interests that are not handled. 31 | # So we can dispatch by forwarding hints. 32 | @app.route('/') 33 | def on_fwd_hint(name: enc.FormalName, app_param: typing.Optional[enc.BinaryStr], 34 | reply: appv2.ReplyFunc, context: appv2.PktContext): 35 | fwd_hints = context["int_param"].forwarding_hint 36 | if fwd_hints: 37 | fh_name = fwd_hints[0] 38 | if enc.Name.is_prefix('/repo', fh_name): 39 | print(f'>> Received forwarding hinted Interest: {enc.Name.to_str(fh_name)}') 40 | on_cmd(name, app_param, reply, context) 41 | return 42 | print(f'>> Received wrong Interest') 43 | 44 | 45 | if __name__ == '__main__': 46 | app.run_forever() 47 | -------------------------------------------------------------------------------- /examples/appv2/keychain_cert/fetch_certificate.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2022 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import sys 19 | import logging 20 | from ndn import appv2, types 21 | from ndn import encoding as enc 22 | from ndn.app_support import security_v2 as secv2 23 | 24 | 25 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 26 | datefmt='%Y-%m-%d %H:%M:%S', 27 | level=logging.INFO, 28 | style='{') 29 | 30 | 31 | if len(sys.argv) < 2: 32 | logging.fatal('Please input a KEY or CERT name') 33 | exit(0) 34 | 35 | app = appv2.NDNApp() 36 | 37 | 38 | async def main(): 39 | try: 40 | name = enc.Name.from_str(sys.argv[1]) 41 | can_be_prefix = not (len(name) > 4 and name[-4] == secv2.KEY_COMPONENT) 42 | print(f'Sending Interest {enc.Name.to_str(name)}, ' 43 | f'{enc.InterestParam(must_be_fresh=True, can_be_prefix=can_be_prefix, lifetime=6000)}') 44 | # TODO: Write a better validator 45 | data_name, content, pkt_context = await app.express( 46 | name, validator=appv2.pass_all, 47 | must_be_fresh=True, can_be_prefix=can_be_prefix, lifetime=6000) 48 | 49 | print(f'Received Data Name: {enc.Name.to_str(data_name)}') 50 | print(pkt_context['meta_info']) 51 | print(bytes(content) if content else None) 52 | except types.InterestNack as e: 53 | print(f'Nacked with reason={e.reason}') 54 | except types.InterestTimeout: 55 | print(f'Timeout') 56 | except types.InterestCanceled: 57 | print(f'Canceled') 58 | except types.ValidationFailure: 59 | print(f'Data failed to validate') 60 | finally: 61 | app.shutdown() 62 | 63 | 64 | if __name__ == '__main__': 65 | app.run_forever(after_start=main()) 66 | -------------------------------------------------------------------------------- /examples/appv2/keychain_cert/keychain_register.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2022 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import logging 19 | from ndn import appv2 20 | from ndn.app_support.keychain_register import attach_keychain_register 21 | 22 | 23 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 24 | datefmt='%Y-%m-%d %H:%M:%S', 25 | level=logging.INFO, 26 | style='{') 27 | 28 | 29 | app = appv2.NDNApp() 30 | keychain = app.default_keychain() 31 | attach_keychain_register(keychain, app) 32 | 33 | 34 | if __name__ == '__main__': 35 | app.run_forever() 36 | -------------------------------------------------------------------------------- /examples/catchunks.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import logging 19 | import sys 20 | from ndn.app import NDNApp 21 | from ndn.app_support.segment_fetcher import segment_fetcher 22 | 23 | 24 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 25 | datefmt='%Y-%m-%d %H:%M:%S', 26 | level=logging.INFO, 27 | style='{') 28 | app = NDNApp() 29 | 30 | 31 | async def main(): 32 | cnt = 0 33 | async for seg in segment_fetcher(app, sys.argv[1]): 34 | print(bytes(seg).decode(), end='') 35 | cnt += 1 36 | print(f'\n{cnt} segments fetched.') 37 | app.shutdown() 38 | 39 | 40 | if __name__ == '__main__': 41 | if len(sys.argv) <= 1: 42 | print(f'Usage: {sys.argv[0]} ') 43 | exit(0) 44 | app.run_forever(after_start=main()) 45 | -------------------------------------------------------------------------------- /examples/consumer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import logging 19 | import ndn.utils 20 | from ndn.app import NDNApp 21 | from ndn.types import InterestNack, InterestTimeout, InterestCanceled, ValidationFailure 22 | from ndn.encoding import Name, Component, InterestParam 23 | 24 | 25 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 26 | datefmt='%Y-%m-%d %H:%M:%S', 27 | level=logging.INFO, 28 | style='{') 29 | 30 | 31 | app = NDNApp() 32 | 33 | 34 | async def main(): 35 | try: 36 | timestamp = ndn.utils.timestamp() 37 | name = Name.from_str('/example/testApp/randomData') + [Component.from_timestamp(timestamp)] 38 | print(f'Sending Interest {Name.to_str(name)}, {InterestParam(must_be_fresh=True, lifetime=6000)}') 39 | data_name, meta_info, content = await app.express_interest( 40 | name, must_be_fresh=True, can_be_prefix=False, lifetime=6000) 41 | 42 | print(f'Received Data Name: {Name.to_str(data_name)}') 43 | print(meta_info) 44 | print(bytes(content) if content else None) 45 | except InterestNack as e: 46 | print(f'Nacked with reason={e.reason}') 47 | except InterestTimeout: 48 | print(f'Timeout') 49 | except InterestCanceled: 50 | print(f'Canceled') 51 | except ValidationFailure: 52 | print(f'Data failed to validate') 53 | finally: 54 | app.shutdown() 55 | 56 | 57 | if __name__ == '__main__': 58 | app.run_forever(after_start=main()) 59 | -------------------------------------------------------------------------------- /examples/dpdk_experimental/udp_consumer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2023 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import logging 19 | import sys 20 | from ndn import utils, appv2, types 21 | from ndn import encoding as enc 22 | from ndn.transport.ndn_dpdk import NdnDpdkUdpFace, DpdkRegisterer 23 | 24 | 25 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 26 | datefmt='%Y-%m-%d %H:%M:%S', 27 | level=logging.INFO, 28 | style='{') 29 | 30 | 31 | # Usage example: python udp_consumer.py http://localhost:3030 172.17.0.1 9001 172.17.0.2 9001 32 | if len(sys.argv) < 6: 33 | print('Insufficient argument') 34 | sys.exit(-1) 35 | 36 | gql_url = sys.argv[1] 37 | self_addr = sys.argv[2] 38 | self_port = int(sys.argv[3]) 39 | dpdk_addr = sys.argv[4] 40 | dpdk_port = int(sys.argv[5]) 41 | 42 | face = NdnDpdkUdpFace(gql_url, self_addr, self_port, dpdk_addr, dpdk_port) 43 | registerer = DpdkRegisterer(face) 44 | 45 | app = appv2.NDNApp(face=face, registerer=registerer) 46 | keychain = app.default_keychain() 47 | 48 | 49 | async def main(): 50 | try: 51 | timestamp = utils.timestamp() 52 | name = enc.Name.from_str('/example/testApp/randomData') + [enc.Component.from_timestamp(timestamp)] 53 | print(f'Sending Interest {enc.Name.to_str(name)}, {enc.InterestParam(must_be_fresh=True, lifetime=6000)}') 54 | # TODO: Write a better validator 55 | data_name, content, pkt_context = await app.express( 56 | name, validator=appv2.pass_all, 57 | must_be_fresh=True, can_be_prefix=False, lifetime=6000) 58 | 59 | print(f'Received Data Name: {enc.Name.to_str(data_name)}') 60 | print(pkt_context['meta_info']) 61 | print(bytes(content) if content else None) 62 | except types.InterestNack as e: 63 | print(f'Nacked with reason={e.reason}') 64 | except types.InterestTimeout: 65 | print(f'Timeout') 66 | except types.InterestCanceled: 67 | print(f'Canceled') 68 | except types.ValidationFailure: 69 | print(f'Data failed to validate') 70 | finally: 71 | app.shutdown() 72 | 73 | 74 | if __name__ == '__main__': 75 | app.run_forever(after_start=main()) 76 | -------------------------------------------------------------------------------- /examples/dpdk_experimental/udp_producer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2023 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import typing 19 | import logging 20 | import sys 21 | from ndn import appv2 22 | from ndn import encoding as enc 23 | from ndn.transport.ndn_dpdk import NdnDpdkUdpFace, DpdkRegisterer 24 | 25 | 26 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 27 | datefmt='%Y-%m-%d %H:%M:%S', 28 | level=logging.INFO, 29 | style='{') 30 | 31 | 32 | # Usage example: python udp_producer.py http://localhost:3030 172.17.0.1 9000 172.17.0.2 9000 33 | if len(sys.argv) < 6: 34 | print('Insufficient argument') 35 | sys.exit(-1) 36 | 37 | gql_url = sys.argv[1] 38 | self_addr = sys.argv[2] 39 | self_port = int(sys.argv[3]) 40 | dpdk_addr = sys.argv[4] 41 | dpdk_port = int(sys.argv[5]) 42 | 43 | face = NdnDpdkUdpFace(gql_url, self_addr, self_port, dpdk_addr, dpdk_port) 44 | registerer = DpdkRegisterer(face) 45 | 46 | app = appv2.NDNApp(face=face, registerer=registerer) 47 | keychain = app.default_keychain() 48 | 49 | 50 | @app.route('/example/testApp') 51 | def on_interest(name: enc.FormalName, _app_param: typing.Optional[enc.BinaryStr], 52 | reply: appv2.ReplyFunc, context: appv2.PktContext): 53 | print(f'>> I: {enc.Name.to_str(name)}, {context["int_param"]}') 54 | content = "Hello, world!".encode() 55 | reply(app.make_data(name, content=content, signer=keychain.get_signer({}), 56 | freshness_period=10000)) 57 | print(f'<< D: {enc.Name.to_str(name)}') 58 | print(enc.MetaInfo(freshness_period=10000)) 59 | print(f'Content: (size: {len(content)})') 60 | print('') 61 | 62 | 63 | if __name__ == '__main__': 64 | app.run_forever() 65 | -------------------------------------------------------------------------------- /examples/lvs/consumer.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import logging 4 | from ndn.utils import timestamp 5 | from ndn.encoding import Name, Component, InterestParam 6 | from ndn.security import TpmFile, KeychainSqlite3 7 | from ndn.app import NDNApp, InterestNack, InterestTimeout, InterestCanceled, ValidationFailure 8 | from ndn.app_support.light_versec import compile_lvs, Checker, DEFAULT_USER_FNS, lvs_validator 9 | 10 | 11 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 12 | datefmt='%Y-%m-%d %H:%M:%S', 13 | level=logging.INFO, 14 | style='{') 15 | 16 | lvs_text = r''' 17 | #KEY: "KEY"/_/_/_ 18 | #site: "lvs-test" 19 | #article: #site/"article"/author/post/_version & {_version: $eq_type("v=0")} <= #author 20 | #author: #site/"author"/author/"KEY"/_/admin/_ <= #admin 21 | #admin: #site/"admin"/admin/#KEY <= #root 22 | #root: #site/#KEY 23 | ''' 24 | 25 | 26 | def main(): 27 | basedir = os.path.dirname(os.path.abspath(sys.argv[0])) 28 | tpm_path = os.path.join(basedir, 'privKeys') 29 | pib_path = os.path.join(basedir, 'pib.db') 30 | keychain = KeychainSqlite3(pib_path, TpmFile(tpm_path)) 31 | 32 | trust_anchor = keychain['/lvs-test'].default_key().default_cert() 33 | print(f'Trust anchor name: {Name.to_str(trust_anchor.name)}') 34 | 35 | lvs_model = compile_lvs(lvs_text) 36 | checker = Checker(lvs_model, DEFAULT_USER_FNS) 37 | app = NDNApp(keychain=keychain) 38 | validator = lvs_validator(checker, app, trust_anchor.data) 39 | 40 | async def fetch_interest(article: str): 41 | try: 42 | name = Name.from_str(f'/lvs-test/article/xinyu/{article}') 43 | print(f'Sending Interest {Name.to_str(name)}') 44 | data_name, meta_info, content = await app.express_interest( 45 | name, must_be_fresh=True, can_be_prefix=True, lifetime=6000, 46 | validator=validator) 47 | print(f'Received Data Name: {Name.to_str(data_name)}') 48 | print(meta_info) 49 | print(bytes(content).decode() if content else None) 50 | except InterestNack as e: 51 | print(f'Nacked with reason={e.reason}') 52 | except InterestTimeout: 53 | print(f'Timeout') 54 | except InterestCanceled: 55 | print(f'Canceled') 56 | except ValidationFailure: 57 | print(f'Data failed to validate') 58 | 59 | async def ndn_main(): 60 | await fetch_interest('hello') 61 | await fetch_interest('world') 62 | 63 | app.shutdown() 64 | 65 | app.run_forever(ndn_main()) 66 | 67 | 68 | if __name__ == '__main__': 69 | main() 70 | -------------------------------------------------------------------------------- /examples/lvs/ls-certificates.sh: -------------------------------------------------------------------------------- 1 | pyndnsec --path . --tpm tpm-file --tpm-path privKeys ls -vvv 2 | -------------------------------------------------------------------------------- /examples/lvs/pib.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/python-ndn/440f65d092a544f5e5571cc71b7f4861c702a3f7/examples/lvs/pib.db -------------------------------------------------------------------------------- /examples/lvs/privKeys/97cc0c81777e988284f8030c4dfaef1f828daf64971ef1bcaa8a4ca3b1f50cbf.privkey: -------------------------------------------------------------------------------- 1 | MHcCAQEEIF7ZR5mKgKfnBQ6XMPDijYNH0UKNjLPnBbqBBXi1jb+xoAoGCCqGSM49 2 | AwEHoUQDQgAENFhT28Rsqhfaej7PcfHIy5Sf9oU5JzRuhp/NuyURonOx0I9ely8g 3 | XbaLqeYq6asyPBaqIp+US8Z1FPIbGTKTxA== -------------------------------------------------------------------------------- /examples/lvs/privKeys/a3e2e001a71e797847dab7f406fd450b4ceb18ab3261a8cf2c498958bf35f17f.privkey: -------------------------------------------------------------------------------- 1 | MHcCAQEEIPyFI6RdO9vhZeFAKJbovhMYht7Ex7gGxsrFfWNZJofYoAoGCCqGSM49 2 | AwEHoUQDQgAEALGvaZ7m2uRLAYA/J0PFIUkDoyuRyW+kGmL8yY2b2xO6+0RhiF4S 3 | iuY1pv980c0c3lVccdIP++pk02xt9KIcTQ== -------------------------------------------------------------------------------- /examples/lvs/privKeys/af3b726c4c32c9de141e6a35865bbbc3a8b2b12d85e27910d8f908e05c47d201.privkey: -------------------------------------------------------------------------------- 1 | MHcCAQEEIPzqwLIknTXhBbVUTPFtLIpUrpqL0+qPDswmWLR/beLgoAoGCCqGSM49 2 | AwEHoUQDQgAE6OHeZi1CDis43xtih2zU/fv7KfFsa4l/k74NyIBSTXm/cYfmQIzF 3 | nPK8IHqPfm43/PzaQltgN7LU1GI/hTPSSg== -------------------------------------------------------------------------------- /examples/nfd_status.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import asyncio as aio 19 | from ndn.app import NDNApp 20 | from ndn.types import InterestNack, InterestTimeout, InterestCanceled, ValidationFailure, NetworkError 21 | from ndn.encoding import Name, is_binary_str 22 | from ndn.app_support.nfd_mgmt import GeneralStatus 23 | import logging 24 | import time 25 | 26 | 27 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 28 | datefmt='%Y-%m-%d %H:%M:%S', 29 | level=logging.DEBUG, 30 | style='{') 31 | 32 | 33 | app = NDNApp() 34 | 35 | 36 | def decode_dict(msg): 37 | ret = msg.asdict() 38 | for k, v in ret.items(): 39 | if is_binary_str(v): 40 | ret[k] = bytes(v).decode() 41 | else: 42 | ret[k] = str(v) 43 | return ret 44 | 45 | 46 | async def retry(): 47 | while True: 48 | try: 49 | name = Name.from_str('/localhost/nfd/status/general') 50 | print(f'Sending Interest') 51 | data_name, meta_info, content = await app.express_interest( 52 | name, must_be_fresh=True, can_be_prefix=True, lifetime=60000) 53 | print(bytes(content) if content else None) 54 | msg = GeneralStatus.parse(content) 55 | status = decode_dict(msg) 56 | print(status) 57 | 58 | except InterestNack as e: 59 | print(f'Nacked with reason={e.reason}') 60 | except InterestTimeout: 61 | print(f'Timeout') 62 | except (InterestCanceled, NetworkError): 63 | print(f'Canceled') 64 | break 65 | except ValidationFailure: 66 | print(f'Data failed to validate') 67 | await aio.sleep(1.0) 68 | print('Finished run') 69 | 70 | 71 | def main(): 72 | running = True 73 | while running: 74 | print('Connecting') 75 | app_main = retry() 76 | try: 77 | running = app.run_forever(after_start=app_main) 78 | except (FileNotFoundError, ConnectionRefusedError): 79 | app_main.close() 80 | if running: 81 | time.sleep(1.0) 82 | 83 | 84 | if __name__ == '__main__': 85 | main() 86 | -------------------------------------------------------------------------------- /examples/producer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from typing import Optional 19 | from ndn.app import NDNApp 20 | from ndn.encoding import Name, InterestParam, BinaryStr, FormalName, MetaInfo 21 | import logging 22 | 23 | 24 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 25 | datefmt='%Y-%m-%d %H:%M:%S', 26 | level=logging.INFO, 27 | style='{') 28 | 29 | 30 | app = NDNApp() 31 | 32 | 33 | @app.route('/example/testApp') 34 | def on_interest(name: FormalName, param: InterestParam, _app_param: Optional[BinaryStr]): 35 | print(f'>> I: {Name.to_str(name)}, {param}') 36 | content = "Hello, world!".encode() 37 | app.put_data(name, content=content, freshness_period=10000) 38 | print(f'<< D: {Name.to_str(name)}') 39 | print(MetaInfo(freshness_period=10000)) 40 | print(f'Content: (size: {len(content)})') 41 | print('') 42 | 43 | 44 | if __name__ == '__main__': 45 | app.run_forever() 46 | -------------------------------------------------------------------------------- /examples/putchunks.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import logging 19 | import sys 20 | from ndn.utils import timestamp 21 | from ndn.app import NDNApp 22 | from ndn.encoding import Name, Component 23 | 24 | SEGMENT_SIZE = 4400 25 | 26 | 27 | def main(): 28 | if len(sys.argv) <= 2: 29 | print(f'Usage: {sys.argv[0]} ') 30 | exit(0) 31 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 32 | datefmt='%Y-%m-%d %H:%M:%S', 33 | level=logging.INFO, 34 | style='{') 35 | 36 | app = NDNApp() 37 | name = Name.normalize(sys.argv[1]) 38 | name.append(Component.from_version(timestamp())) 39 | 40 | with open(sys.argv[2], 'rb') as f: 41 | data = f.read() 42 | seg_cnt = (len(data) + SEGMENT_SIZE - 1) // SEGMENT_SIZE 43 | packets = [app.prepare_data(name + [Component.from_segment(i)], 44 | data[i*SEGMENT_SIZE:(i+1)*SEGMENT_SIZE], 45 | freshness_period=10000, 46 | final_block_id=Component.from_segment(seg_cnt - 1)) 47 | for i in range(seg_cnt)] 48 | print(f'Created {seg_cnt} chunks under name {Name.to_str(name)}') 49 | 50 | @app.route(name) 51 | def on_interest(int_name, _int_param, _app_param): 52 | if Component.get_type(int_name[-1]) == Component.TYPE_SEGMENT: 53 | seg_no = Component.to_number(int_name[-1]) 54 | else: 55 | seg_no = 0 56 | if seg_no < seg_cnt: 57 | app.put_raw_packet(packets[seg_no]) 58 | 59 | app.run_forever() 60 | 61 | 62 | if __name__ == '__main__': 63 | main() 64 | -------------------------------------------------------------------------------- /examples/rdrnode.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import sys 19 | import asyncio as aio 20 | import logging 21 | from ndn.app import NDNApp 22 | from ndn.encoding import Name 23 | from ndn.schema import policy 24 | from ndn.schema.schema_tree import Node 25 | from ndn.schema.simple_node import RDRNode 26 | from ndn.schema.simple_cache import MemoryCache, MemoryCachePolicy 27 | from ndn.schema.simple_trust import SignedBy 28 | 29 | 30 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 31 | datefmt='%Y-%m-%d %H:%M:%S', 32 | level=logging.INFO, 33 | style='{') 34 | app = NDNApp() 35 | 36 | 37 | async def main(): 38 | if len(sys.argv) <= 1: 39 | print(f'Usage: {sys.argv[0]} []') 40 | exit(0) 41 | 42 | # Make schema tree 43 | root = Node() 44 | root['//KEY//self/'] = Node() 45 | root['/file/'] = RDRNode() 46 | 47 | # Set policies 48 | id_name = Name.Component.get_value(app.keychain.default_identity().name[0]) 49 | cache = MemoryCache() 50 | root.set_policy(policy.Cache, MemoryCachePolicy(cache)) 51 | root['/file/'].set_policy( 52 | policy.DataValidator, 53 | SignedBy(root['//KEY/'], 54 | subject_to=lambda _, vars: vars['IDName'] == id_name)) 55 | 56 | # Store the certificate 57 | cert = app.keychain.default_identity().default_key().default_cert() 58 | await cache.save(Name.normalize(cert.name), cert.data) 59 | 60 | # Attach the tree to the face 61 | await root.attach(app, '/') 62 | 63 | filename = sys.argv[1] 64 | if len(sys.argv) > 2: 65 | # If it's the producer 66 | filepath = sys.argv[2] 67 | print(f'Read {filename} from file {filepath}...') 68 | # Provider with file 69 | with open(filepath, 'rb') as f: 70 | data = f.read() 71 | await root.match('/file/' + filename).provide(data, freshness_period=60000) 72 | # Wait for it to be cached 73 | await aio.sleep(0.1) 74 | else: 75 | # If it's the producer 76 | print(f'Try to fetch {filename}...') 77 | 78 | # The file is ready! 79 | data, metadata = await root.match('/file/' + filename).need() 80 | print(f'Content size: {len(data)}') 81 | print(f'Content: {data[:70]} ...') 82 | print(f'Number of segments: {metadata["block_count"]}') 83 | print(f'Serving {filename}') 84 | 85 | if __name__ == '__main__': 86 | app.run_forever(after_start=main()) 87 | -------------------------------------------------------------------------------- /examples/rpc_consumer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import sys 19 | import logging 20 | import ndn.utils 21 | from ndn.app import NDNApp 22 | from ndn.types import InterestNack, InterestTimeout, InterestCanceled, ValidationFailure 23 | from ndn.encoding import Name, Component, InterestParam 24 | 25 | 26 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 27 | datefmt='%Y-%m-%d %H:%M:%S', 28 | level=logging.INFO, 29 | style='{') 30 | 31 | 32 | app = NDNApp() 33 | 34 | 35 | async def main(): 36 | try: 37 | app_param = ' '.join(x for x in sys.argv[1:]) 38 | timestamp = ndn.utils.timestamp() 39 | name = Name.from_str('/example/rpc') + [Component.from_timestamp(timestamp)] 40 | print(f'Sending Interest {Name.to_str(name)}, ' 41 | f'{InterestParam(must_be_fresh=True, lifetime=6000)}, ' 42 | f'{app_param}') 43 | data_name, meta_info, content = await app.express_interest( 44 | name, app_param.encode(), must_be_fresh=True, can_be_prefix=False, lifetime=6000) 45 | 46 | print(f'Received Data Name: {Name.to_str(data_name)}') 47 | print(meta_info) 48 | print(bytes(content) if content else None) 49 | except InterestNack as e: 50 | print(f'Nacked with reason={e.reason}') 51 | except InterestTimeout: 52 | print(f'Timeout') 53 | except InterestCanceled: 54 | print(f'Canceled') 55 | except ValidationFailure: 56 | print(f'Data failed to validate') 57 | finally: 58 | app.shutdown() 59 | 60 | 61 | if __name__ == '__main__': 62 | app.run_forever(after_start=main()) 63 | -------------------------------------------------------------------------------- /examples/rpc_producer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from typing import Optional 19 | from ndn.app import NDNApp 20 | from ndn.encoding import Name, InterestParam, BinaryStr, FormalName, MetaInfo 21 | import logging 22 | 23 | 24 | logging.basicConfig(format='[{asctime}]{levelname}:{message}', 25 | datefmt='%Y-%m-%d %H:%M:%S', 26 | level=logging.INFO, 27 | style='{') 28 | 29 | 30 | app = NDNApp() 31 | 32 | 33 | @app.route('/example/rpc') 34 | def on_interest(name: FormalName, param: InterestParam, app_param: Optional[BinaryStr]): 35 | app_param = bytes(app_param) 36 | print(f'>> I: {Name.to_str(name)}, {param}, {app_param}') 37 | if not app_param: 38 | print("<< No application parameter, dropped") 39 | return 40 | s = sum(int(x) for x in app_param.split()) 41 | content = str(s).encode() 42 | app.put_data(name, content=content, freshness_period=500) 43 | print(f'<< D: {Name.to_str(name)}') 44 | print(MetaInfo(freshness_period=500)) 45 | print(f'Content: {content}') 46 | print('') 47 | 48 | 49 | if __name__ == '__main__': 50 | app.run_forever() 51 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["poetry-core>=1.6.0","poetry-dynamic-versioning>=0.22.0"] 3 | build-backend = "poetry_dynamic_versioning.backend" 4 | 5 | [tool.poetry-dynamic-versioning] 6 | enable = true 7 | bump = true 8 | 9 | [tool.poetry-dynamic-versioning.substitution] 10 | files = ["src/ndn/__init__.py"] 11 | 12 | [tool.poetry] 13 | name = "python-ndn" 14 | version = "0.0.0" 15 | description = "An NDN client library with AsyncIO support in Python 3" 16 | authors = ["Xinyu Ma "] 17 | license = "Apache-2.0" 18 | readme = ["README.rst", "CHANGELOG.rst"] 19 | homepage = "https://python-ndn.readthedocs.io" 20 | repository = "https://github.com/named-data/python-ndn" 21 | documentation = "https://python-ndn.readthedocs.io" 22 | keywords = ["NDN"] 23 | classifiers = [ 24 | "Development Status :: 4 - Beta", 25 | "Intended Audience :: Developers", 26 | "Topic :: Software Development :: Libraries", 27 | "Topic :: Internet", 28 | "Topic :: System :: Networking", 29 | "License :: OSI Approved :: Apache Software License", 30 | "Programming Language :: Python :: 3.11", 31 | "Programming Language :: Python :: 3.12", 32 | "Programming Language :: Python :: 3.13", 33 | ] 34 | packages = [{ include = "ndn", from = "src" }] 35 | include = [{ path = "tests", format = "sdist" }] 36 | 37 | [tool.poetry.urls] 38 | "Bug Tracker" = "https://github.com/named-data/python-ndn/issues" 39 | "Changelog" = "https://python-ndn.readthedocs.io/en/latest/src/changelog.html" 40 | 41 | [tool.poetry.dependencies] 42 | python = "^3.10" 43 | pycryptodomex = "^3.21.0" 44 | pygtrie = "^2.5.0" 45 | aenum = "^3.1.15" 46 | lark = "^1.2.2" 47 | aiohttp = "^3.10.9" 48 | 49 | # Extra dependencies [dev] 50 | pytest = { version = "^7.1.2", optional = true } 51 | pytest-cov = { version = "^4.1.0", optional = true } 52 | flake8 = { version = "^6.1.0", optional = true } 53 | 54 | # Extra dependencies [docs] 55 | Sphinx = { version = "^7.1.2", optional = true } 56 | sphinx-rtd-theme = { version = "^1.3.0rc1", optional = true } 57 | sphinx-autodoc-typehints = { version = "^1.24.0", optional = true } 58 | 59 | [tool.poetry.extras] 60 | dev = ["pytest", "pytest-cov", "flake8"] 61 | docs = ["Sphinx", "sphinx-rtd-theme", "sphinx-autodoc-typehints"] 62 | 63 | [tool.poetry.scripts] 64 | pyndnsec = "ndn.bin.sec:main" 65 | pyndntools = "ndn.bin.tools:main" 66 | pynfdc = "ndn.bin.nfdc:main" 67 | -------------------------------------------------------------------------------- /src/ndn/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.0.0" 2 | -------------------------------------------------------------------------------- /src/ndn/app_support/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/python-ndn/440f65d092a544f5e5571cc71b7f4861c702a3f7/src/ndn/app_support/__init__.py -------------------------------------------------------------------------------- /src/ndn/app_support/dispatcher.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from typing import Optional 19 | from ..encoding import NonStrictName, Name, BinaryStr, InterestParam, FormalName 20 | from ..types import Route 21 | from ..name_tree import NameTrie, PrefixTreeNode 22 | 23 | 24 | class Dispatcher: 25 | """ 26 | An Interest dispatcher that helps a producer application further dispatches Interests under some route. 27 | """ 28 | 29 | _tree: NameTrie = None 30 | 31 | def __init__(self): 32 | self._tree = NameTrie() 33 | 34 | def register(self, name: NonStrictName, func: Route): 35 | """ 36 | Register a callback function. This will not register an NDN route. 37 | 38 | :param name: the name prefix. 39 | :param func: the callback function. 40 | :raises ValueError: the name prefix is already registered. 41 | """ 42 | name = Name.normalize(name) 43 | node = self._tree.setdefault(name, PrefixTreeNode()) 44 | if node.callback: 45 | raise ValueError(f'Duplicated registration: {Name.to_str(name)}') 46 | node.callback = func 47 | 48 | def unregister(self, name: NonStrictName): 49 | """ 50 | Unregister a callback function. 51 | 52 | :param name: the name prefix. 53 | """ 54 | name = Name.normalize(name) 55 | del self._tree[name] 56 | 57 | def dispatch(self, name: FormalName, param: InterestParam, app_param: Optional[BinaryStr]) -> bool: 58 | """ 59 | Dispatch the Interest to registered callbacks using longest match. 60 | 61 | :return: ``True`` if the Interest is dispatched to some callbacks. 62 | """ 63 | trie_step = self._tree.longest_prefix(name) 64 | if not trie_step: 65 | return False 66 | trie_step.value.callback(name, param, app_param) 67 | return True 68 | -------------------------------------------------------------------------------- /src/ndn/app_support/keychain_register.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2022 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import logging 19 | from ..appv2 import NDNApp, ReplyFunc 20 | from .. import security as sec 21 | from .. import encoding as enc 22 | from . import security_v2 as secv2 23 | 24 | 25 | class KcHandler: 26 | ident: sec.AbstractIdentity 27 | 28 | def on_int(self, int_name: enc.FormalName, _app_param, reply: ReplyFunc, pkt_ctx): 29 | logger = logging.getLogger(__name__) 30 | id_name = self.ident.name 31 | if not enc.Name.is_prefix(id_name, int_name): 32 | return 33 | can_be_prefix = pkt_ctx['int_param'].can_be_prefix 34 | # can_be_prefix = True if using KEY name, False if using CERT name 35 | if len(int_name) != len(id_name) + (2 if can_be_prefix else 4): 36 | logger.warning(f'Invalid key fetching Interest: {enc.Name.to_str(int_name)}') 37 | return 38 | try: 39 | key_name = int_name[:len(id_name)+2] 40 | key = self.ident[key_name] 41 | cert = None 42 | if can_be_prefix: 43 | # fetch KEY 44 | for _, cur_cert in key.items(): 45 | cert = cur_cert 46 | break 47 | else: 48 | cert = key[int_name] 49 | if cert is not None: 50 | logger.info(f'KeychainRegister replied with: {enc.Name.to_str(cert.name)}') 51 | reply(cert.data) 52 | else: 53 | logger.warning(f'No certificate for key: {enc.Name.to_str(int_name)}') 54 | except KeyError: 55 | logger.warning(f'Fetching not existing key/cert: {enc.Name.to_str(int_name)}') 56 | 57 | def __init__(self, ident: sec.AbstractIdentity): 58 | self.ident = ident 59 | 60 | 61 | def attach_keychain_register(keychain: sec.Keychain, app: NDNApp): 62 | for name, ident in keychain.items(): 63 | reg_name = name + [secv2.KEY_COMPONENT] 64 | handler = KcHandler(ident) 65 | app.route(reg_name)(handler.on_int) 66 | -------------------------------------------------------------------------------- /src/ndn/app_support/light_versec/__init__.py: -------------------------------------------------------------------------------- 1 | from .checker import * 2 | from .binary import * 3 | from .compiler import * 4 | from .validator import * 5 | 6 | __all__ = [] 7 | __all__.extend(checker.__all__) 8 | __all__.extend(binary.__all__) 9 | __all__.extend(compiler.__all__) 10 | -------------------------------------------------------------------------------- /src/ndn/app_support/light_versec/grammar.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # This piece of work is inspired by Pollere' VerSec: 3 | # https://github.com/pollere/DCT 4 | # But this code is implemented independently without using any line of the 5 | # original one, and released under Apache License. 6 | # 7 | # Copyright (C) 2019-2022 The python-ndn authors 8 | # 9 | # This file is part of python-ndn. 10 | # 11 | # Licensed under the Apache License, Version 2.0 (the "License"); 12 | # you may not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | # ----------------------------------------------------------------------------- 23 | lvs_grammar = r''' 24 | ?start: file_input 25 | 26 | TAG_IDENT: CNAME 27 | RULE_IDENT: "#" CNAME 28 | FN_IDENT: "$" CNAME 29 | 30 | name: "/"? component ("/" component)* 31 | component: STR -> component_from_str 32 | | TAG_IDENT -> tag_id 33 | | RULE_IDENT -> rule_id 34 | 35 | definition: RULE_IDENT ":" def_expr 36 | def_expr: name ("&" comp_constraints)? ("<=" sign_constraints)? 37 | sign_constraints: RULE_IDENT ("|" RULE_IDENT)* 38 | comp_constraints: cons_set ("|" cons_set)* 39 | cons_set: "{" cons_term ("," cons_term)* "}" 40 | cons_term: TAG_IDENT ":" cons_expr 41 | cons_expr: cons_option ("|" cons_option)* 42 | cons_option: STR -> component_from_str 43 | | TAG_IDENT -> tag_id 44 | | FN_IDENT "(" fn_args ")" -> fn_call 45 | fn_args: (STR | TAG_IDENT)? ("," (STR | TAG_IDENT))* 46 | 47 | file_input: definition* 48 | 49 | %import common (DIGIT, LETTER, WS, CNAME, CPP_COMMENT) 50 | %import common.ESCAPED_STRING -> STR 51 | 52 | %ignore WS 53 | %ignore CPP_COMMENT 54 | ''' 55 | -------------------------------------------------------------------------------- /src/ndn/app_support/light_versec/validator.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # This piece of work is inspired by Pollere' VerSec: 3 | # https://github.com/pollere/DCT 4 | # But this code is implemented independently without using any line of the 5 | # original one, and released under Apache License. 6 | # 7 | # Copyright (C) 2019-2022 The python-ndn authors 8 | # 9 | # This file is part of python-ndn. 10 | # 11 | # Licensed under the Apache License, Version 2.0 (the "License"); 12 | # you may not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | # ----------------------------------------------------------------------------- 23 | import logging 24 | from ...encoding import BinaryStr, SignaturePtrs, FormalName, parse_data, Name 25 | from ...app import NDNApp, Validator 26 | from ...security import union_checker 27 | from ...security.validator.cascade_validator import CascadeChecker, PublicKeyStorage, MemoryKeyStorage 28 | from .checker import Checker 29 | 30 | __all__ = ['lvs_validator'] 31 | 32 | 33 | def lvs_validator(checker: Checker, app: NDNApp, trust_anchor: BinaryStr, 34 | storage: PublicKeyStorage = MemoryKeyStorage()) -> Validator: 35 | async def validate_name(name: FormalName, sig_ptrs: SignaturePtrs) -> bool: 36 | if (not sig_ptrs.signature_info or not sig_ptrs.signature_info.key_locator 37 | or not sig_ptrs.signature_info.key_locator.name): 38 | return False 39 | cert_name = sig_ptrs.signature_info.key_locator.name 40 | logging.getLogger(__name__).debug(f'LVS Checking {Name.to_str(name)} <- {Name.to_str(cert_name)} ...') 41 | return checker.check(name, cert_name) 42 | 43 | def sanity_check(): 44 | root_of_trust = checker.root_of_trust() 45 | if not checker.validate_user_fns(): 46 | raise ValueError('Missing user functions for LVS validator') 47 | cert_name, _, _, _ = parse_data(trust_anchor) 48 | ta_matches = sum((m[0] for m in checker.match(cert_name)), start=[]) 49 | if not ta_matches or not root_of_trust.issubset(ta_matches): 50 | raise ValueError('Trust anchor does not match all roots of trust of LVS model') 51 | 52 | sanity_check() 53 | cas_checker = CascadeChecker(app, trust_anchor, storage) 54 | ret = union_checker(validate_name, cas_checker) 55 | cas_checker.next_level = ret 56 | return ret 57 | -------------------------------------------------------------------------------- /src/ndn/app_support/segment_fetcher.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from ..encoding import NonStrictName, Name, Component 19 | from ..app import NDNApp 20 | from ..types import InterestTimeout 21 | 22 | 23 | async def segment_fetcher(app: NDNApp, name: NonStrictName, timeout=4000, retry_times=3, 24 | validator=None, must_be_fresh=True): 25 | """ 26 | An async-generator to fetch a segmented object. Interests are issued one by one. 27 | 28 | :param app: NDN Application 29 | :param name: Name prefix of Data 30 | :param timeout: Timeout value, in milliseconds 31 | :param retry_times: Times for retry 32 | :param validator: Validator 33 | :param must_be_fresh: MustBeFresh field of Interest 34 | :return: Data segments in order. 35 | """ 36 | async def retry(first): 37 | nonlocal name 38 | trial_times = 0 39 | while True: 40 | future = app.express_interest(name, validator=validator, can_be_prefix=first, 41 | must_be_fresh=must_be_fresh, lifetime=timeout) 42 | try: 43 | return await future 44 | except InterestTimeout: 45 | trial_times += 1 46 | if trial_times >= retry_times: 47 | raise 48 | 49 | name = Name.normalize(name) 50 | # First Interest 51 | name, meta, content = await retry(True) 52 | # If it's not segmented 53 | if Component.get_type(name[-1]) != Component.TYPE_SEGMENT: 54 | yield content 55 | return 56 | # If it's segmented 57 | if Component.to_number(name[-1]) == 0: 58 | yield content 59 | if meta.final_block_id == name[-1]: 60 | return 61 | seg_no = 1 62 | else: 63 | # If it's not segment 0, starting from 0 64 | seg_no = 0 65 | # Following Interests 66 | while True: 67 | name[-1] = Component.from_segment(seg_no) 68 | name, meta, content = await retry(False) 69 | yield content 70 | if meta.final_block_id == name[-1]: 71 | return 72 | seg_no += 1 73 | -------------------------------------------------------------------------------- /src/ndn/app_support/svs/__init__.py: -------------------------------------------------------------------------------- 1 | from .tlv import * 2 | from .sync import * 3 | 4 | __all__ = [] 5 | __all__.extend(tlv.__all__) 6 | __all__.extend(sync.__all__) 7 | -------------------------------------------------------------------------------- /src/ndn/app_support/svs/tlv.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2023-2023 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from ... import encoding as enc 19 | 20 | 21 | __all__ = ['StateVecEntry', 'StateVec', 'StateVecWrapper', 'MappingEntry', 'MappingData', 'MappingDataWrapper'] 22 | 23 | 24 | class StateVecEntry(enc.TlvModel): 25 | node_id = enc.NameField() 26 | seq_no = enc.UintField(0xcc) 27 | 28 | 29 | class StateVec(enc.TlvModel): 30 | entries = enc.RepeatedField(enc.ModelField(0xca, StateVecEntry)) 31 | 32 | 33 | class StateVecWrapper(enc.TlvModel): 34 | val = enc.ModelField(0xc9, StateVec) 35 | 36 | 37 | class MappingEntry(enc.TlvModel): 38 | seq_no = enc.UintField(0xcc) 39 | app_name = enc.NameField() 40 | 41 | 42 | class MappingData(enc.TlvModel): 43 | node_id = enc.NameField() 44 | entries = enc.ModelField(0xce, MappingEntry) 45 | 46 | 47 | class MappingDataWrapper(enc.TlvModel): 48 | val = enc.ModelField(0xcd, MappingEntry) 49 | -------------------------------------------------------------------------------- /src/ndn/bin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/python-ndn/440f65d092a544f5e5571cc71b7f4861c702a3f7/src/ndn/bin/__init__.py -------------------------------------------------------------------------------- /src/ndn/bin/nfdc/__init__.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | from . import cmd_get_status, cmd_get_face, cmd_get_route, cmd_get_strategy, \ 20 | cmd_remove_face, cmd_new_face, cmd_remove_route, cmd_new_route, cmd_remove_strategy, cmd_set_strategy 21 | 22 | 23 | CMD_LIST = ''' 24 | Available commands: 25 | Get-Status (status) 26 | Get-Face (face,gf) 27 | New-Face (nf) 28 | Remove-Face (rf) 29 | Get-Route (route,gr) 30 | New-Route (nr) 31 | Remove-Route (rr) 32 | Get-Strategy (strategy,gs) 33 | Set-Strategy (ss) 34 | Remove-Strategy (rs) 35 | 36 | Try '%(prog)s COMMAND -h' for more information on each command 37 | ''' 38 | 39 | 40 | def main(): 41 | parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, epilog=CMD_LIST) 42 | subparsers = parser.add_subparsers(metavar='COMMAND', help='sub-command to execute') 43 | 44 | cmd_get_status.add_parser(subparsers) 45 | cmd_get_face.add_parser(subparsers) 46 | cmd_get_route.add_parser(subparsers) 47 | cmd_get_strategy.add_parser(subparsers) 48 | cmd_remove_face.add_parser(subparsers) 49 | cmd_new_face.add_parser(subparsers) 50 | cmd_remove_route.add_parser(subparsers) 51 | cmd_new_route.add_parser(subparsers) 52 | cmd_remove_strategy.add_parser(subparsers) 53 | cmd_set_strategy.add_parser(subparsers) 54 | 55 | args = parser.parse_args() 56 | if 'executor' not in args: 57 | parser.print_help() 58 | exit(-1) 59 | 60 | return args.executor(args) 61 | -------------------------------------------------------------------------------- /src/ndn/bin/nfdc/__main__.py: -------------------------------------------------------------------------------- 1 | from . import main 2 | if __name__ == '__main__': 3 | main() 4 | -------------------------------------------------------------------------------- /src/ndn/bin/nfdc/cmd_get_route.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | from ...appv2 import NDNApp 20 | from ...encoding import Name 21 | from ...app_support.nfd_mgmt import FibStatus, RibStatus 22 | from .utils import express_interest 23 | 24 | 25 | def add_parser(subparsers): 26 | parser = subparsers.add_parser('Get-Route', aliases=['route', 'gr']) 27 | parser.add_argument('route', metavar='ROUTE', nargs='?', default='', 28 | help='The prefix of the specified route to query') 29 | parser.set_defaults(executor=execute) 30 | 31 | 32 | def execute(args: argparse.Namespace): 33 | app = NDNApp() 34 | route = args.route 35 | 36 | async def list_route(): 37 | try: 38 | fib_data = await express_interest(app, "/localhost/nfd/fib/list") 39 | fib_msg = FibStatus.parse(fib_data) 40 | rib_data = await express_interest(app, "/localhost/nfd/rib/list") 41 | rib_msg = RibStatus.parse(rib_data) 42 | # TODO: Should calculate the length instead of using a fixed number 43 | print('Forwarding Table (FIB)') 44 | for ent in fib_msg.entries: 45 | prefix = Name.to_str(ent.name) 46 | if route and prefix != route: 47 | continue 48 | print(prefix) 49 | for nh in ent.next_hop_records: 50 | print(f'\tFaceID={nh.face_id:<5} Cost={nh.cost:<5}') 51 | print() 52 | print('Routing Table (RIB)') 53 | for ent in rib_msg.entries: 54 | prefix = Name.to_str(ent.name) 55 | if route and prefix != route: 56 | continue 57 | print(prefix) 58 | for nh in ent.routes: 59 | print(f'\tFaceID={nh.face_id:<5} Cost={nh.cost:<5} Origin={nh.origin:<3} Flags={nh.flags}') 60 | finally: 61 | app.shutdown() 62 | 63 | app.run_forever(list_route()) 64 | -------------------------------------------------------------------------------- /src/ndn/bin/nfdc/cmd_get_status.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | import datetime 20 | from ...appv2 import NDNApp 21 | from ...app_support.nfd_mgmt import GeneralStatus 22 | from .utils import express_interest 23 | 24 | 25 | def add_parser(subparsers): 26 | parser = subparsers.add_parser('Get-Status', aliases=['status']) 27 | parser.set_defaults(executor=execute) 28 | 29 | 30 | def execute(_args: argparse.Namespace): 31 | app = NDNApp() 32 | 33 | async def after_start(): 34 | try: 35 | data = await express_interest(app, "/localhost/nfd/status/general") 36 | 37 | msg = GeneralStatus.parse(data) 38 | 39 | print('General status:') 40 | print(f'{"version":>25}\t{msg.nfd_version}') 41 | st_time = datetime.datetime.fromtimestamp(msg.start_timestamp / 1000) 42 | print(f'{"startTime":>25}\t{st_time.strftime("%Y-%m-%d %H:%M:%S.%f")}') 43 | cur_time = datetime.datetime.fromtimestamp(msg.current_timestamp / 1000) 44 | print(f'{"currentTime":>25}\t{cur_time.strftime("%Y-%m-%d %H:%M:%S.%f")}') 45 | up_time = cur_time - st_time 46 | print(f'{"upTime":>25}\t{up_time}') 47 | print(f'{"nNameTreeEntries":>25}\t{msg.n_name_tree_entries}') 48 | print(f'{"nFibEntries":>25}\t{msg.n_fib_entries}') 49 | print(f'{"nPitEntries":>25}\t{msg.n_pit_entries}') 50 | print(f'{"nMeasurementsEntries":>25}\t{msg.n_measurement_entries}') 51 | print(f'{"nCsEntries":>25}\t{msg.n_cs_entries}') 52 | print(f'{"nInInterests":>25}\t{msg.n_in_interests}') 53 | print(f'{"nOutInterests":>25}\t{msg.n_out_interests}') 54 | print(f'{"nInData":>25}\t{msg.n_in_data}') 55 | print(f'{"nOutData":>25}\t{msg.n_out_data}') 56 | print(f'{"nInNacks":>25}\t{msg.n_in_nacks}') 57 | print(f'{"nOutNacks":>25}\t{msg.n_out_nacks}') 58 | print(f'{"nSatisfiedInterests":>25}\t{msg.n_satisfied_interests}') 59 | print(f'{"nUnsatisfiedInterests":>25}\t{msg.n_unsatisfied_interests}') 60 | finally: 61 | app.shutdown() 62 | 63 | app.run_forever(after_start()) 64 | -------------------------------------------------------------------------------- /src/ndn/bin/nfdc/cmd_get_strategy.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | from ...appv2 import NDNApp 20 | from ...encoding import Name 21 | from ...app_support.nfd_mgmt import StrategyChoiceMsg 22 | from .utils import express_interest 23 | 24 | 25 | def add_parser(subparsers): 26 | parser = subparsers.add_parser('Get-Strategy', aliases=['strategy', 'gs']) 27 | parser.add_argument('prefix', metavar='PREFIX', nargs='?', default='', 28 | help='The specified route prefix') 29 | parser.set_defaults(executor=execute) 30 | 31 | 32 | def execute(args: argparse.Namespace): 33 | app = NDNApp() 34 | prefix = args.prefix 35 | 36 | async def list_strategy(): 37 | try: 38 | data = await express_interest(app, "/localhost/nfd/strategy-choice/list") 39 | msg = StrategyChoiceMsg.parse(data) 40 | for s in msg.strategy_choices: 41 | s_prefix = Name.to_str(s.name) 42 | if prefix and s_prefix != prefix: 43 | continue 44 | print(f'{s_prefix}\n\t{Name.to_str(s.strategy.name)}') 45 | finally: 46 | app.shutdown() 47 | 48 | app.run_forever(list_strategy()) 49 | -------------------------------------------------------------------------------- /src/ndn/bin/nfdc/cmd_new_face.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | from ...appv2 import NDNApp 20 | from ...app_support.nfd_mgmt import parse_response, make_command_v2 21 | from .utils import express_interest 22 | 23 | 24 | def add_parser(subparsers): 25 | parser = subparsers.add_parser('New-Face', aliases=['nf']) 26 | parser.add_argument('uri', metavar='URI', 27 | help='The URI or IP address of the face to create. ' 28 | 'Note: current version does not support DNS resolve.') 29 | parser.set_defaults(executor=execute) 30 | 31 | 32 | def execute(args: argparse.Namespace): 33 | app = NDNApp() 34 | uri = str(args.uri) 35 | if uri.find('://') < 0: 36 | uri = 'udp4://' + uri 37 | if len(uri.split(":")) < 3: 38 | uri = uri + ":6363" 39 | 40 | async def create_face(): 41 | cmd = make_command_v2('faces', 'create', uri=uri.encode()) 42 | res = await express_interest(app, cmd) 43 | msg = parse_response(res) 44 | print(f'{msg["status_code"]} {msg["status_text"]}') 45 | app.shutdown() 46 | 47 | app.run_forever(after_start=create_face()) 48 | -------------------------------------------------------------------------------- /src/ndn/bin/nfdc/cmd_new_route.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | from ...appv2 import NDNApp 20 | from ...app_support.nfd_mgmt import make_command_v2, parse_response 21 | from .utils import express_interest 22 | 23 | 24 | def add_parser(subparsers): 25 | parser = subparsers.add_parser('New-Route', aliases=['nr']) 26 | parser.add_argument('route', metavar='ROUTE', 27 | help='The prefix of new or existing route') 28 | parser.add_argument('face_id', metavar='FACE_ID', 29 | help='The next-hop to add') 30 | parser.set_defaults(executor=execute) 31 | 32 | 33 | def execute(args: argparse.Namespace): 34 | app = NDNApp() 35 | route = args.route 36 | face_id = args.face_id 37 | 38 | async def register_route(): 39 | try: 40 | fid = int(face_id) 41 | cmd = make_command_v2('rib', 'register', name=route, face_id=fid) 42 | res = await express_interest(app, cmd) 43 | msg = parse_response(res) 44 | print(f'{msg["status_code"]} {msg["status_text"]}') 45 | finally: 46 | app.shutdown() 47 | 48 | app.run_forever(after_start=register_route()) 49 | -------------------------------------------------------------------------------- /src/ndn/bin/nfdc/cmd_remove_face.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | from ...appv2 import NDNApp 20 | from ...encoding import Name, Component 21 | from ...app_support.nfd_mgmt import FaceStatusMsg, FaceQueryFilter, FaceQueryFilterValue, parse_response, \ 22 | make_command_v2 23 | from .utils import express_interest 24 | 25 | 26 | def add_parser(subparsers): 27 | parser = subparsers.add_parser('Remove-Face', aliases=['rf']) 28 | parser.add_argument('face', metavar='FACE', 29 | help='FaceID or URI of specified face') 30 | parser.set_defaults(executor=execute) 31 | 32 | 33 | def execute(args: argparse.Namespace): 34 | app = NDNApp() 35 | face = args.face 36 | 37 | async def remove_face(fid): 38 | print(f'Removing face {fid} ...', end='') 39 | cmd = make_command_v2('faces', 'destroy', face_id=fid) 40 | res = await express_interest(app, cmd) 41 | msg = parse_response(res) 42 | print(f'\t{msg["status_code"]} {msg["status_text"]}') 43 | 44 | async def run_with_fid(fid): 45 | try: 46 | await remove_face(fid) 47 | finally: 48 | app.shutdown() 49 | 50 | async def run_with_uri(uri): 51 | async def try_remove(): 52 | data = await express_interest(app, data_name) 53 | if not data: 54 | return False 55 | elif data[0] == 0x65: 56 | msg = parse_response(data) 57 | print('Query failed with response', msg['status_code'], msg['status_text']) 58 | else: 59 | msg = FaceStatusMsg.parse(data) 60 | for f in msg.face_status: 61 | await remove_face(f.face_id) 62 | return True 63 | 64 | try: 65 | name = "/localhost/nfd/faces/query" 66 | filt = FaceQueryFilter() 67 | filt.face_query_filter = FaceQueryFilterValue() 68 | filt.face_query_filter.uri = uri 69 | data_name = Name.from_str(name) + [Component.from_bytes(filt.encode())] 70 | if not await try_remove(): 71 | filt.face_query_filter.uri = None 72 | filt.face_query_filter.local_uri = uri 73 | data_name = Name.from_str(name) + [Component.from_bytes(filt.encode())] 74 | if not await try_remove(): 75 | print('No face is found') 76 | finally: 77 | app.shutdown() 78 | 79 | try: 80 | face_id = int(face) 81 | app.run_forever(after_start=run_with_fid(face_id)) 82 | except ValueError: 83 | app.run_forever(after_start=run_with_uri(face)) 84 | -------------------------------------------------------------------------------- /src/ndn/bin/nfdc/cmd_remove_route.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | from ...appv2 import NDNApp 20 | from ...app_support.nfd_mgmt import parse_response, make_command_v2 21 | from .utils import express_interest 22 | 23 | 24 | def add_parser(subparsers): 25 | parser = subparsers.add_parser('Remove-Route', aliases=['rr']) 26 | parser.add_argument('route', metavar='ROUTE', 27 | help='The prefix of the specified route to remove') 28 | parser.add_argument('face_id', metavar='FACE_ID', nargs='?', default='', 29 | help='Specific next-hop to remove') 30 | parser.set_defaults(executor=execute) 31 | 32 | 33 | def execute(args: argparse.Namespace): 34 | app = NDNApp() 35 | route = args.route 36 | face_id = args.face_id 37 | 38 | async def remove_route(): 39 | try: 40 | if not face_id: 41 | cmd = make_command_v2('rib', 'unregister', name=route) 42 | else: 43 | fid = int(face_id) 44 | cmd = make_command_v2('rib', 'unregister', name=route, face_id=fid) 45 | res = await express_interest(app, cmd) 46 | msg = parse_response(res) 47 | print(f'{msg["status_code"]} {msg["status_text"]}') 48 | finally: 49 | app.shutdown() 50 | 51 | app.run_forever(after_start=remove_route()) 52 | -------------------------------------------------------------------------------- /src/ndn/bin/nfdc/cmd_remove_strategy.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | from ...appv2 import NDNApp 20 | from ...app_support.nfd_mgmt import parse_response, make_command_v2 21 | from .utils import express_interest 22 | 23 | 24 | def add_parser(subparsers): 25 | parser = subparsers.add_parser('Remove-Strategy', aliases=['rs']) 26 | parser.add_argument('prefix', metavar='PREFIX', 27 | help='The prefix of the specified strategy choice to remove') 28 | parser.set_defaults(executor=execute) 29 | 30 | 31 | def execute(args: argparse.Namespace): 32 | app = NDNApp() 33 | prefix = args.prefix 34 | 35 | async def remove_strategy(): 36 | try: 37 | cmd = make_command_v2('strategy-choice', 'unset', name=prefix) 38 | res = await express_interest(app, cmd) 39 | msg = parse_response(res) 40 | print(f'{msg["status_code"]} {msg["status_text"]}') 41 | finally: 42 | app.shutdown() 43 | 44 | app.run_forever(after_start=remove_strategy()) 45 | -------------------------------------------------------------------------------- /src/ndn/bin/nfdc/cmd_set_strategy.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | from ...appv2 import NDNApp 20 | from ...app_support.nfd_mgmt import parse_response, make_command_v2 21 | from .utils import express_interest 22 | 23 | 24 | def add_parser(subparsers): 25 | parser = subparsers.add_parser('Set-Strategy', aliases=['ss']) 26 | parser.add_argument('prefix', metavar='PREFIX', 27 | help='The prefix of the specified strategy choice to remove') 28 | parser.add_argument('strategy', metavar='STRATEGY', 29 | help='The name of strategy. Can be [multicast, best-route, access, asf, self-learning, ncc]' 30 | ' or the full name of any supported strategy') 31 | parser.set_defaults(executor=execute) 32 | 33 | 34 | STRATEGY_FULLNAME = { 35 | 'multicast': '/localhost/nfd/strategy/multicast', 36 | 'best-route': '/localhost/nfd/strategy/best-route', 37 | 'access': '/localhost/nfd/strategy/access', 38 | 'asf': '/localhost/nfd/strategy/asf', 39 | 'self-learning': '/localhost/nfd/strategy/self-learning', 40 | 'ncc': '/localhost/nfd/strategy/ncc', 41 | } 42 | 43 | 44 | def execute(args: argparse.Namespace): 45 | app = NDNApp() 46 | prefix = args.prefix 47 | strategy = args.strategy 48 | if strategy in STRATEGY_FULLNAME: 49 | strategy = STRATEGY_FULLNAME[strategy] 50 | 51 | async def remove_strategy(): 52 | try: 53 | cmd = make_command_v2('strategy-choice', 'set', name=prefix, strategy=strategy) 54 | res = await express_interest(app, cmd) 55 | msg = parse_response(res) 56 | print(f'{msg["status_code"]} {msg["status_text"]}') 57 | finally: 58 | app.shutdown() 59 | 60 | app.run_forever(after_start=remove_strategy()) 61 | -------------------------------------------------------------------------------- /src/ndn/bin/nfdc/utils.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from ...appv2 import NDNApp, pass_all 19 | from ...security import DigestSha256Signer 20 | from ...types import InterestNack, InterestTimeout, InterestCanceled, ValidationFailure 21 | 22 | 23 | async def express_interest(app: NDNApp, name): 24 | try: 25 | _, data, context = await app.express( 26 | name, validator=pass_all, app_param=b'', signer=DigestSha256Signer(True), 27 | lifetime=1000, can_be_prefix=True, must_be_fresh=True) 28 | return data 29 | except InterestNack as e: 30 | print(f'Nacked with reason={e.reason}') 31 | exit(-1) 32 | except InterestTimeout: 33 | print('Timeout') 34 | exit(-1) 35 | except InterestCanceled: 36 | print('Local forwarder disconnected') 37 | exit(-1) 38 | except ValidationFailure: 39 | print('Data failed to validate') 40 | exit(-1) 41 | -------------------------------------------------------------------------------- /src/ndn/bin/sec/__main__.py: -------------------------------------------------------------------------------- 1 | from . import main 2 | if __name__ == '__main__': 3 | main() 4 | -------------------------------------------------------------------------------- /src/ndn/bin/sec/cmd_export_cert.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | import base64 20 | from .utils import resolve_keychain, get_default_cert 21 | 22 | 23 | def add_parser(subparsers): 24 | parser = subparsers.add_parser('Export-Cert', aliases=['export', 'ec', 'export-cert']) 25 | parser.add_argument('obj', metavar='OBJECT', nargs='?', default='', 26 | help='name of the identity/key/certificate to export.') 27 | parser.set_defaults(executor=execute) 28 | 29 | 30 | def execute(args: argparse.Namespace): 31 | kc = resolve_keychain(args) 32 | cert = get_default_cert(kc, args) 33 | if cert is None: 34 | return -2 35 | 36 | text = base64.standard_b64encode(bytes(cert.data)).decode() 37 | cnt = (len(text) + 63) // 64 38 | for i in range(cnt): 39 | print(text[i * 64:(i + 1) * 64]) 40 | -------------------------------------------------------------------------------- /src/ndn/bin/sec/cmd_get_default.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | from ...encoding import Name 20 | from .utils import resolve_keychain, infer_obj_name 21 | 22 | 23 | def add_parser(subparsers): 24 | parser = subparsers.add_parser('Get-Default', aliases=['gd', 'get-default']) 25 | parser.add_argument('-k', '--key', dest='verbose', action='store_const', const=1, default=0, 26 | help='show default key, instead of identity') 27 | parser.add_argument('-c', '--cert', dest='verbose', action='store_const', const=2, default=0, 28 | help='show default certificate, instead of identity') 29 | parser.add_argument('obj', metavar='OBJECT', nargs='?', default='', 30 | help='target identity or key') 31 | parser.set_defaults(executor=execute) 32 | 33 | 34 | def execute(args: argparse.Namespace): 35 | kc = resolve_keychain(args) 36 | verbose = args.verbose 37 | obj = args.obj 38 | id_name = [] 39 | key_name = [] 40 | cert_name = [] 41 | 42 | if obj: 43 | obj_name = Name.from_str(obj) 44 | _, id_name, key_name, cert_name = infer_obj_name(obj_name) 45 | 46 | try: 47 | if id_name: 48 | iden = kc[id_name] 49 | else: 50 | iden = kc.default_identity() 51 | except KeyError as e: 52 | print('Requested identity does not exist.') 53 | print(f'KeyError: {e}') 54 | return -1 55 | if verbose <= 0: 56 | print(Name.to_str(iden.name)) 57 | return 0 58 | 59 | try: 60 | if key_name: 61 | key = iden[key_name] 62 | else: 63 | key = iden.default_key() 64 | except KeyError as e: 65 | print('Requested key does not exist.') 66 | print(f'KeyError: {e}') 67 | return -1 68 | if verbose == 1: 69 | print(Name.to_str(key.name)) 70 | return 0 71 | 72 | try: 73 | if cert_name: 74 | cert = key[cert_name] 75 | else: 76 | cert = key.default_cert() 77 | except KeyError as e: 78 | print('Requested certificate does not exist.') 79 | print(f'KeyError: {e}') 80 | return -1 81 | print(Name.to_str(cert.name)) 82 | return 0 83 | -------------------------------------------------------------------------------- /src/ndn/bin/sec/cmd_get_signreq.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | import base64 20 | from ...encoding import Name 21 | from ...app_support.security_v2 import sign_req 22 | from .utils import resolve_keychain, infer_obj_name 23 | 24 | 25 | def add_parser(subparsers): 26 | parser = subparsers.add_parser('Get-SignReq', aliases=['sign-req', 'get-signreq', 'gsr']) 27 | parser.add_argument('obj', metavar='OBJECT', nargs='?', default='', 28 | help='identity or key name') 29 | parser.set_defaults(executor=execute) 30 | 31 | 32 | def execute(args: argparse.Namespace): 33 | kc = resolve_keychain(args) 34 | obj = args.obj 35 | id_name = [] 36 | key_name = [] 37 | 38 | if obj: 39 | obj_name = Name.from_str(obj) 40 | _, id_name, key_name, cert_name = infer_obj_name(obj_name) 41 | 42 | try: 43 | if id_name: 44 | iden = kc[id_name] 45 | else: 46 | iden = kc.default_identity() 47 | except KeyError as e: 48 | print('Requested identity does not exist.') 49 | print(f'KeyError: {e}') 50 | return -2 51 | 52 | try: 53 | if key_name: 54 | key = iden[key_name] 55 | else: 56 | key = iden.default_key() 57 | except KeyError as e: 58 | print('Requested key does not exist.') 59 | print(f'KeyError: {e}') 60 | return -2 61 | 62 | _, data = sign_req(key.name, key.key_bits, kc.get_signer({'key': key.name})) 63 | text = base64.standard_b64encode(bytes(data)).decode() 64 | cnt = (len(text) + 63) // 64 65 | for i in range(cnt): 66 | print(text[i * 64:(i + 1) * 64]) 67 | -------------------------------------------------------------------------------- /src/ndn/bin/sec/cmd_import_cert.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import base64 19 | import os 20 | import sys 21 | import argparse 22 | from ...encoding import Name 23 | from ...app_support.security_v2 import parse_certificate 24 | from .utils import resolve_keychain 25 | 26 | 27 | def add_parser(subparsers): 28 | parser = subparsers.add_parser('Import-Cert', aliases=['import', 'ic', 'import-cert']) 29 | parser.add_argument('file', metavar='FILE', nargs='?', default='-', 30 | help="file name of the certificate to be imported, '-' for stdin") 31 | parser.set_defaults(executor=execute) 32 | 33 | 34 | def execute(args: argparse.Namespace): 35 | kc = resolve_keychain(args) 36 | if args.file == '-': 37 | text = sys.stdin.read() 38 | else: 39 | with open(os.path.expandvars(args.file), 'r') as f: 40 | text = f.read() 41 | 42 | try: 43 | cert_data = base64.standard_b64decode(text) 44 | cert = parse_certificate(cert_data) 45 | except (ValueError, IndexError): 46 | print('Malformed certificate') 47 | return -1 48 | 49 | cert_name = cert.name 50 | key_name = cert_name[:-2] 51 | id_name = cert_name[:-4] 52 | try: 53 | key = kc[id_name][key_name] 54 | except KeyError: 55 | print(f'Specified key {Name.to_str(key_name)} does not exist.') 56 | return -2 57 | try: 58 | _ = key[cert_name] 59 | print(f'Specified certificate {Name.to_str(cert_name)} already exists.') 60 | return -2 61 | except KeyError: 62 | pass 63 | kc.import_cert(key_name, cert_name, cert_data) 64 | -------------------------------------------------------------------------------- /src/ndn/bin/sec/cmd_init_pib.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import os 19 | import sys 20 | import argparse 21 | from ...platform import Platform 22 | from ...security import KeychainSqlite3 23 | 24 | 25 | def add_parser(subparsers): 26 | parser = subparsers.add_parser('Init-Pib', aliases=['init', 'init-pib']) 27 | parser.set_defaults(executor=execute) 28 | 29 | 30 | def execute(args: argparse.Namespace): 31 | tpm = args.tpm 32 | tpm_path = args.tpm_path 33 | base_dir = args.path 34 | platform = Platform() 35 | if not tpm: 36 | tpm = platform.default_tpm_scheme() 37 | if tpm == 'tpm-osxkeychain' and sys.platform != 'darwin': 38 | print(f'ERROR: {tpm} only works on MacOS.') 39 | return -2 40 | if tpm == 'tpm-cng' and sys.platform != 'win32': 41 | print(f'ERROR: {tpm} only works on Windows 10/11 with a TPM chip.') 42 | return -2 43 | if not base_dir: 44 | base_dir = platform.default_pib_paths()[0] 45 | pib_path = os.path.join(base_dir, 'pib.db') 46 | if not tpm_path: 47 | if tpm == 'tpm-file': 48 | tpm_path = os.path.join(base_dir, 'ndnsec-key-file') 49 | else: 50 | tpm_path = '' 51 | print(f'Initializing PIB at {pib_path}, with tpm-locator={tpm}:{tpm_path}') 52 | ret = KeychainSqlite3.initialize(pib_path, tpm, tpm_path) 53 | if ret: 54 | print('Successfully created PIB. Before running any NDN application, ' 55 | 'Please make sure you have the correct client.conf and a default key.') 56 | return 0 57 | else: 58 | print('Failed to create PIB database.') 59 | return -1 60 | -------------------------------------------------------------------------------- /src/ndn/bin/sec/cmd_new_item.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | from ...encoding import Name 20 | from .utils import resolve_keychain, infer_obj_name 21 | 22 | 23 | def add_parser(subparsers): 24 | parser = subparsers.add_parser('New-Item', aliases=['key-gen', 'ni', 'new-item']) 25 | parser.add_argument('-t', '--type', metavar='TYPE', default='e', choices=['e', 'r'], 26 | help="key type: 'r' for RSA, 'e' for ECDSA (default: e)") 27 | parser.add_argument('-k', '--keyid-type', metavar='KEYIDTYPE', default='r', choices=['h', 'r'], 28 | help="key id type: 'h' for the SHA-256 of the public key, " 29 | "'r' for a 64-bit random number (the default unless " 30 | "a key name is specified for OBJECT)") 31 | parser.add_argument('obj', metavar='OBJECT', help='identity/key name') 32 | parser.set_defaults(executor=execute) 33 | 34 | 35 | def execute(args: argparse.Namespace): 36 | kc = resolve_keychain(args) 37 | obj_name = Name.from_str(args.obj) 38 | _, id_name, key_name, cert_name = infer_obj_name(obj_name) 39 | 40 | # New identity 41 | try: 42 | iden = kc[id_name] 43 | except KeyError: 44 | iden = kc.new_identity(id_name) 45 | print(f'Created new identity: {Name.to_str(id_name)}') 46 | 47 | # New key 48 | if key_name: 49 | try: 50 | _ = iden[key_name] 51 | print(f'Specified key already exists: {Name.to_str(key_name)}') 52 | return -2 53 | except KeyError: 54 | key = None 55 | else: 56 | key = None 57 | if key is None: 58 | key_type = 'ec' if args.type == 'e' else 'rsa' 59 | if key_name: 60 | key = kc.new_key(id_name, key_type, key_id=key_name[-1]) 61 | else: 62 | key_id_type = 'sha256' if args.keyid_type == 'h' else 'random' 63 | key = kc.new_key(id_name, key_type, key_id_type=key_id_type) 64 | key_name = key.name 65 | print(f'Created new key: {Name.to_str(key_name)}') 66 | print(f'With self-signed certificate: {Name.to_str(key.default_cert().name)}') 67 | -------------------------------------------------------------------------------- /src/ndn/bin/sec/cmd_remove_item.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | from ...encoding import Name 20 | from .utils import resolve_keychain, infer_obj_name 21 | 22 | 23 | def add_parser(subparsers): 24 | parser = subparsers.add_parser('Remove-Item', aliases=['del', 'rm', 'ri', 'remove-item']) 25 | parser.add_argument('obj', metavar='OBJECT', 26 | help='name of the identity/key/certificate to delete.') 27 | parser.set_defaults(executor=execute) 28 | 29 | 30 | def execute(args: argparse.Namespace): 31 | kc = resolve_keychain(args) 32 | obj_name = Name.from_str(args.obj) 33 | v, id_name, key_name, cert_name = infer_obj_name(obj_name) 34 | 35 | if v <= 0: 36 | kc.del_identity(id_name) 37 | print(f'Deleted identity {Name.to_str(id_name)}') 38 | elif v == 1: 39 | kc.del_key(key_name) 40 | print(f'Deleted key {Name.to_str(key_name)}') 41 | else: 42 | kc.del_cert(cert_name) 43 | print(f'Deleted certificate {Name.to_str(cert_name)}') 44 | -------------------------------------------------------------------------------- /src/ndn/bin/sec/cmd_set_default.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | from ...encoding import Name 20 | from .utils import resolve_keychain, infer_obj_name 21 | 22 | 23 | def add_parser(subparsers): 24 | parser = subparsers.add_parser('Set-Default', aliases=['sd', 'set-default']) 25 | parser.add_argument(dest='obj', metavar='OBJECT', 26 | help='the identity/key/certificate name to set') 27 | parser.set_defaults(executor=execute) 28 | 29 | 30 | def execute(args: argparse.Namespace): 31 | kc = resolve_keychain(args) 32 | obj_name = Name.from_str(args.obj) 33 | v, id_name, key_name, cert_name = infer_obj_name(obj_name) 34 | 35 | try: 36 | iden = kc[id_name] 37 | except KeyError as e: 38 | print('Requested identity does not exist.') 39 | print(f'KeyError: {e}') 40 | return -1 41 | if v <= 0: 42 | kc.set_default_identity(id_name) 43 | return 0 44 | 45 | try: 46 | key = iden[key_name] 47 | except KeyError as e: 48 | print('Requested key does not exist.') 49 | print(f'KeyError: {e}') 50 | return -1 51 | if v <= 1: 52 | iden.set_default_key(key_name) 53 | return 0 54 | 55 | try: 56 | _ = key[cert_name] 57 | except KeyError as e: 58 | print('Requested certificate does not exist.') 59 | print(f'KeyError: {e}') 60 | return -1 61 | key.set_default_cert(cert_name) 62 | return 0 63 | -------------------------------------------------------------------------------- /src/ndn/bin/tools/__init__.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | 20 | from . import ( 21 | cmd_compile_lvs, 22 | cmd_fetch_data, 23 | cmd_fetch_rdrcontent, 24 | cmd_serve_data, 25 | cmd_serve_rdrcontent, 26 | ) 27 | 28 | 29 | CMD_LIST = """ 30 | Available commands: 31 | Serve-Data (poke,sd) 32 | Fetch-Data (peek,fd) 33 | Serve-RdrContent (putchunks,src) 34 | Fetch-RdrContent (catchunks,frc) 35 | Compile-Lvs (compile-lvs) 36 | 37 | Try '%(prog)s COMMAND -h' for more information on each command 38 | """ 39 | 40 | 41 | def main(): 42 | parser = argparse.ArgumentParser( 43 | formatter_class=argparse.RawDescriptionHelpFormatter, epilog=CMD_LIST 44 | ) 45 | subparsers = parser.add_subparsers(metavar="COMMAND", help="sub-command to execute") 46 | 47 | cmd_fetch_data.add_parser(subparsers) 48 | cmd_serve_data.add_parser(subparsers) 49 | cmd_fetch_rdrcontent.add_parser(subparsers) 50 | cmd_serve_rdrcontent.add_parser(subparsers) 51 | cmd_compile_lvs.add_parser(subparsers) 52 | 53 | args = parser.parse_args() 54 | if "executor" not in args: 55 | parser.print_help() 56 | exit(-1) 57 | 58 | return args.executor(args) 59 | -------------------------------------------------------------------------------- /src/ndn/bin/tools/__main__.py: -------------------------------------------------------------------------------- 1 | from . import main 2 | if __name__ == '__main__': 3 | main() 4 | -------------------------------------------------------------------------------- /src/ndn/bin/tools/cmd_compile_lvs.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2025-2025 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import argparse 19 | import os 20 | import sys 21 | 22 | import lark 23 | from ndn.app_support.light_versec import compile_lvs, SemanticError 24 | from ndn.app_support.light_versec.binary import * 25 | 26 | 27 | def add_parser(subparsers): 28 | parser = subparsers.add_parser("Compile-Lvs", aliases=["compile-lvs"]) 29 | parser.add_argument( 30 | "-o", 31 | "--output", 32 | metavar="FILE", 33 | default="-", 34 | help="Write compiled result into a file", 35 | ) 36 | parser.add_argument( 37 | "input_file", 38 | metavar="FILE", 39 | nargs="?", 40 | default="-", 41 | help="file containing the content of the LVS text, '-' for stdin (default)", 42 | ) 43 | parser.set_defaults(executor=execute) 44 | 45 | 46 | def execute(args: argparse.Namespace): 47 | try: 48 | if args.input_file == "-": 49 | text = sys.stdin.read() 50 | else: 51 | with open(os.path.expandvars(args.input_file), "r") as f: 52 | text = f.read() 53 | except (ValueError, OSError, IndexError): 54 | print("Unable to read the input file") 55 | return -1 56 | 57 | try: 58 | lvs_model = compile_lvs(text) 59 | except (SemanticError, lark.UnexpectedInput) as e: 60 | print("Unable to compile the LVS text input:", e) 61 | return -2 62 | 63 | encoded_output = bytes(lvs_model.encode()) 64 | 65 | if args.output: 66 | if args.output == "-": 67 | sys.stdout.buffer.write(encoded_output) 68 | else: 69 | with open(os.path.expandvars(args.output), "wb") as f: 70 | f.write(encoded_output) 71 | -------------------------------------------------------------------------------- /src/ndn/bin/tools/cmd_serve_data.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import os 19 | import sys 20 | import argparse 21 | from ...encoding import Name, MetaInfo 22 | from ...appv2 import NDNApp 23 | from ...security import KeychainDigest 24 | 25 | 26 | def add_parser(subparsers): 27 | parser = subparsers.add_parser('Serve-Data', aliases=['poke', 'sd', 'serve-data']) 28 | parser.add_argument('-f', '--freshness', metavar='FRESHNESS', default=60000, type=int, 29 | help='the freshness period of the Data packet') 30 | parser.add_argument('-o', '--output', metavar='FILE', 31 | help='write the AppParam into a file') 32 | # More to be added 33 | parser.add_argument('name', metavar='NAME', 34 | help='the name of the Data packet') 35 | parser.add_argument('file', metavar='FILE', nargs='?', default='-', 36 | help="file containing the content of the Data, '-' for stdin (default)") 37 | parser.set_defaults(executor=execute) 38 | 39 | 40 | def execute(args: argparse.Namespace): 41 | fresh = args.freshness 42 | try: 43 | name = Name.from_str(args.name) 44 | except (ValueError, IndexError): 45 | print(f'Invalid name: {args.name}') 46 | return -1 47 | 48 | try: 49 | if args.file == '-': 50 | text = sys.stdin.read().encode() 51 | else: 52 | with open(os.path.expandvars(args.file), 'rb') as f: 53 | text = f.read() 54 | except (ValueError, OSError, IndexError): 55 | print('Unable to read the input file') 56 | return -2 57 | 58 | app = NDNApp() 59 | keychain = KeychainDigest() 60 | 61 | @app.route(name) 62 | def on_interest(int_name, app_param, reply, context): 63 | print(f'>> I: {Name.to_str(int_name)}, {context["int_param"]}') 64 | if app_param: 65 | print(f'AppParam: (size: {len(bytes(app_param))})') 66 | if args.output: 67 | if args.output == '-': 68 | print(bytes(app_param).decode()) 69 | else: 70 | with open(os.path.expandvars(args.output), 'wb') as f: 71 | f.write(bytes(app_param)) 72 | content = text 73 | reply(app.make_data(name, signer=keychain.get_signer({}), content=content, freshness_period=fresh)) 74 | print(f'<< D: {Name.to_str(name)}') 75 | print(MetaInfo(freshness_period=fresh)) 76 | print(f'Content: (size: {len(content)})') 77 | print('') 78 | 79 | print(f'Start serving {Name.to_str(name)} ...') 80 | app.run_forever() 81 | -------------------------------------------------------------------------------- /src/ndn/contrib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/python-ndn/440f65d092a544f5e5571cc71b7f4861c702a3f7/src/ndn/contrib/__init__.py -------------------------------------------------------------------------------- /src/ndn/contrib/boost_info/__init__.py: -------------------------------------------------------------------------------- 1 | from .parser import PropertyTree, PropertyNode 2 | 3 | __all__ = ['PropertyTree', 'PropertyNode'] 4 | -------------------------------------------------------------------------------- /src/ndn/contrib/cocoapy/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006-2008 Alex Holkner 2 | Copyright (c) 2008-2019 pyglet contributorsAll rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in 11 | the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of pyglet nor the names of its 14 | contributors may be used to endorse or promote products 15 | derived from this software without specific prior written 16 | permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 21 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 28 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/ndn/contrib/cocoapy/__init__.py: -------------------------------------------------------------------------------- 1 | # objective-ctypes 2 | # 3 | # Copyright (c) 2011, Phillip Nguyen 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions 8 | # are met: 9 | # 10 | # Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # Redistributions in binary form must reproduce the above copyright 13 | # notice, this list of conditions and the following disclaimer in the 14 | # documentation and/or other materials provided with the distribution. 15 | # Neither the name of objective-ctypes nor the names of its 16 | # contributors may be used to endorse or promote products derived from 17 | # this software without specific prior written permission. 18 | # 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 | # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | # POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from .runtime import objc, send_message, send_super 33 | from .runtime import get_selector 34 | from .runtime import ObjCClass, ObjCInstance, ObjCSubclass 35 | 36 | from .cocoatypes import * 37 | from .cocoalibs import * 38 | -------------------------------------------------------------------------------- /src/ndn/contrib/cocoapy/cocoatypes.py: -------------------------------------------------------------------------------- 1 | from ctypes import * 2 | 3 | import sys, platform, struct 4 | 5 | __LP64__ = (8*struct.calcsize("P") == 64) 6 | __i386__ = (platform.machine() == 'i386') 7 | 8 | PyObjectEncoding = b'{PyObject=@}' 9 | 10 | def encoding_for_ctype(vartype): 11 | typecodes = {c_char:b'c', c_int:b'i', c_short:b's', c_long:b'l', c_longlong:b'q', 12 | c_ubyte:b'C', c_uint:b'I', c_ushort:b'S', c_ulong:b'L', c_ulonglong:b'Q', 13 | c_float:b'f', c_double:b'd', c_bool:b'B', c_char_p:b'*', c_void_p:b'@', 14 | py_object:PyObjectEncoding} 15 | return typecodes.get(vartype, b'?') 16 | 17 | # Note CGBase.h located at 18 | # /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Headers/CGBase.h 19 | # defines CGFloat as double if __LP64__, otherwise it's a float. 20 | if __LP64__: 21 | NSInteger = c_long 22 | NSUInteger = c_ulong 23 | CGFloat = c_double 24 | NSPointEncoding = b'{CGPoint=dd}' 25 | NSSizeEncoding = b'{CGSize=dd}' 26 | NSRectEncoding = b'{CGRect={CGPoint=dd}{CGSize=dd}}' 27 | NSRangeEncoding = b'{_NSRange=QQ}' 28 | else: 29 | NSInteger = c_int 30 | NSUInteger = c_uint 31 | CGFloat = c_float 32 | NSPointEncoding = b'{_NSPoint=ff}' 33 | NSSizeEncoding = b'{_NSSize=ff}' 34 | NSRectEncoding = b'{_NSRect={_NSPoint=ff}{_NSSize=ff}}' 35 | NSRangeEncoding = b'{_NSRange=II}' 36 | 37 | NSIntegerEncoding = encoding_for_ctype(NSInteger) 38 | NSUIntegerEncoding = encoding_for_ctype(NSUInteger) 39 | CGFloatEncoding = encoding_for_ctype(CGFloat) 40 | 41 | # Special case so that NSImage.initWithCGImage_size_() will work. 42 | CGImageEncoding = b'{CGImage=}' 43 | 44 | NSZoneEncoding = b'{_NSZone=}' 45 | 46 | # from /System/Library/Frameworks/Foundation.framework/Headers/NSGeometry.h 47 | class NSPoint(Structure): 48 | _fields_ = [ ("x", CGFloat), ("y", CGFloat) ] 49 | CGPoint = NSPoint 50 | 51 | class NSSize(Structure): 52 | _fields_ = [ ("width", CGFloat), ("height", CGFloat) ] 53 | CGSize = NSSize 54 | 55 | class NSRect(Structure): 56 | _fields_ = [ ("origin", NSPoint), ("size", NSSize) ] 57 | CGRect = NSRect 58 | 59 | def NSMakeSize(w, h): 60 | return NSSize(w, h) 61 | 62 | def NSMakeRect(x, y, w, h): 63 | return NSRect(NSPoint(x, y), NSSize(w, h)) 64 | 65 | # NSDate.h 66 | NSTimeInterval = c_double 67 | 68 | CFIndex = c_long 69 | UniChar = c_ushort 70 | unichar = c_wchar # (actually defined as c_ushort in NSString.h, but need ctypes to convert properly) 71 | CGGlyph = c_ushort 72 | 73 | # CFRange struct defined in CFBase.h 74 | # This replaces the CFRangeMake(LOC, LEN) macro. 75 | class CFRange(Structure): 76 | _fields_ = [ ("location", CFIndex), ("length", CFIndex) ] 77 | 78 | # NSRange.h (Note, not defined the same as CFRange) 79 | class NSRange(Structure): 80 | _fields_ = [ ("location", NSUInteger), ("length", NSUInteger) ] 81 | 82 | NSZeroPoint = NSPoint(0,0) 83 | 84 | CFTypeID = c_ulong 85 | CFNumberType = c_uint32 86 | -------------------------------------------------------------------------------- /src/ndn/encoding/__init__.py: -------------------------------------------------------------------------------- 1 | from .tlv_type import * 2 | from .tlv_var import * 3 | from .name import * 4 | from .signer import * 5 | from .tlv_model import * 6 | 7 | from .ndn_format_0_3 import * 8 | from .ndnlp_v2 import * 9 | 10 | __all__ = [] 11 | __all__.extend(tlv_type.__all__) 12 | __all__.extend(tlv_var.__all__) 13 | __all__.extend(name.__all__) 14 | __all__.extend(signer.__all__) 15 | __all__.extend(tlv_model.__all__) 16 | 17 | __all__.extend(ndn_format_0_3.__all__) 18 | __all__.extend(ndnlp_v2.__all__) 19 | -------------------------------------------------------------------------------- /src/ndn/encoding/name/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['Component', 'Name'] 2 | -------------------------------------------------------------------------------- /src/ndn/encoding/signer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import abc 19 | from typing import List 20 | from .tlv_type import VarBinaryStr 21 | 22 | 23 | __all__ = ['Signer'] 24 | 25 | 26 | class Signer(metaclass=abc.ABCMeta): 27 | @abc.abstractmethod 28 | def write_signature_info(self, signature_info): 29 | """ 30 | Fill in the fields of SignatureInfo. 31 | 32 | :param signature_info: a blank SignatureInfo object. 33 | """ 34 | pass 35 | 36 | @abc.abstractmethod 37 | def get_signature_value_size(self) -> int: 38 | """ 39 | Get the size of SignatureValue. 40 | If the size is variable, return the maximum possible value. 41 | 42 | :return: the size of SignatureValue. 43 | """ 44 | pass 45 | 46 | @abc.abstractmethod 47 | def write_signature_value(self, wire: VarBinaryStr, contents: List[VarBinaryStr]) -> int: 48 | """ 49 | Calculate the SignatureValue and write it into wire. 50 | The length of wire is exactly what :meth:`get_signature_value_size` returns. 51 | Basically this function should return the same value except for ECDSA. 52 | 53 | :param wire: the buffer to contain SignatureValue. 54 | :param contents: a list of memory blocks that needs to be covered. 55 | :return: the actual size of SignatureValue. 56 | """ 57 | pass 58 | -------------------------------------------------------------------------------- /src/ndn/encoding/tlv_type.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from typing import Union, List, Iterable 19 | 20 | 21 | __all__ = ['BinaryStr', 'VarBinaryStr', 'FormalName', 'NonStrictName', 'is_binary_str'] 22 | 23 | 24 | BinaryStr = Union[bytes, bytearray, memoryview] 25 | r"""A binary string is any of :class:`bytes`, :class:`bytearray`, :class:`memoryview`.""" 26 | 27 | VarBinaryStr = Union[bytearray, memoryview] 28 | r"""A variant binary string is a :class:`bytearray` or a non-readonly :class:`memoryview`.""" 29 | 30 | FormalName = List[BinaryStr] 31 | r"""A FormalName is a list of encoded Components.""" 32 | 33 | NonStrictName = Union[Iterable[Union[BinaryStr, str]], str, BinaryStr] 34 | r""" 35 | A NonStrictName is any of below: 36 | 37 | - A URI string. 38 | - A list or iterator of Components, in the form of either encoded TLV or URI string. 39 | - An encoded Name of type :class:`bytes`, :class:`bytearray` or :class:`memoryview`. 40 | 41 | See also :ref:`label-different-names` 42 | """ 43 | 44 | 45 | def is_binary_str(var): 46 | r""" 47 | Check whether var is of type BinaryStr. 48 | 49 | :param var: The variable to check. 50 | :return: ``True`` if var is a :any:`BinaryStr`. 51 | """ 52 | return isinstance(var, (bytes, bytearray, memoryview)) 53 | -------------------------------------------------------------------------------- /src/ndn/platform/__init__.py: -------------------------------------------------------------------------------- 1 | from .general import * 2 | 3 | __all__ = [] 4 | __all__.extend(general.__all__) 5 | -------------------------------------------------------------------------------- /src/ndn/platform/general.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import abc 19 | import sys 20 | from typing import List 21 | 22 | __all__ = ['Platform'] 23 | 24 | 25 | class Platform(abc.ABC): 26 | _instance = None 27 | 28 | def __new__(cls): 29 | if Platform._instance is None: 30 | if sys.platform == 'darwin': 31 | from .osx import Darwin 32 | target_class = Darwin 33 | elif sys.platform == 'linux': 34 | from .linux import Linux 35 | target_class = Linux 36 | elif sys.platform == 'win32': 37 | from .windows import Win32 38 | target_class = Win32 39 | else: 40 | raise ValueError(f'Unsupported platform: {sys.platform}') 41 | Platform._instance = object.__new__(target_class) 42 | return Platform._instance 43 | 44 | @abc.abstractmethod 45 | def client_conf_paths(self) -> List[str]: 46 | pass 47 | 48 | @abc.abstractmethod 49 | def default_transport(self) -> str: 50 | pass 51 | 52 | @abc.abstractmethod 53 | def default_pib_scheme(self) -> str: 54 | pass 55 | 56 | @abc.abstractmethod 57 | def default_pib_paths(self) -> List[str]: 58 | pass 59 | 60 | @abc.abstractmethod 61 | def default_tpm_scheme(self) -> str: 62 | pass 63 | 64 | @abc.abstractmethod 65 | def default_tpm_paths(self) -> List[str]: 66 | pass 67 | 68 | @abc.abstractmethod 69 | async def open_unix_connection(self, path=None): 70 | pass 71 | -------------------------------------------------------------------------------- /src/ndn/platform/linux.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import os 19 | import asyncio as aio 20 | from .general import Platform 21 | 22 | 23 | class Linux(Platform): 24 | def client_conf_paths(self): 25 | return [os.path.expanduser('~/.ndn/client.conf'), 26 | '/usr/local/etc/ndn/client.conf', 27 | '/opt/local/etc/ndn/client.conf', 28 | '/etc/ndn/client.conf'] 29 | 30 | def default_transport(self): 31 | if not os.path.exists('/run/nfd/nfd.sock') and os.path.exists('/run/nfd.sock'): 32 | # Try to be compatible to old NFD 33 | return 'unix:///run/nfd.sock' 34 | return 'unix:///run/nfd/nfd.sock' 35 | 36 | def default_pib_scheme(self): 37 | return 'pib-sqlite3' 38 | 39 | def default_pib_paths(self): 40 | return [os.path.expanduser(r'~/.ndn')] 41 | 42 | def default_tpm_scheme(self): 43 | return 'tpm-file' 44 | 45 | def default_tpm_paths(self): 46 | return [os.path.expanduser(r'~/.ndn/ndnsec-key-file')] 47 | 48 | async def open_unix_connection(self, path=None): 49 | return await aio.open_unix_connection(path) 50 | -------------------------------------------------------------------------------- /src/ndn/schema/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/python-ndn/440f65d092a544f5e5571cc71b7f4861c702a3f7/src/ndn/schema/__init__.py -------------------------------------------------------------------------------- /src/ndn/schema/simple_cache.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import logging 19 | from ..encoding import FormalName, Name, BinaryStr, InterestParam 20 | from ..name_tree import NameTrie 21 | from .schema_tree import MatchedNode 22 | from . import policy 23 | 24 | 25 | class MemoryCache: 26 | """ 27 | MemoryCache is a simple cache class that supports searching and storing Data packets in the memory. 28 | """ 29 | def __init__(self): 30 | self.data = NameTrie() 31 | 32 | async def search(self, name: FormalName, param: InterestParam): 33 | """ 34 | Search for the data packet that satisfying an Interest packet with name specified. 35 | 36 | :param name: the Interest name. 37 | :param param: the parameters of the Interest. Not used in current implementation. 38 | :return: a raw Data packet or None. 39 | """ 40 | try: 41 | return next(self.data.itervalues(prefix=name, shallow=True)) 42 | except KeyError: 43 | logging.getLogger(__name__).debug(f'Cache miss: {Name.to_str(name)}') 44 | return None 45 | 46 | async def save(self, name: FormalName, packet: BinaryStr): 47 | """ 48 | Save a Data packet with name into the memory storage. 49 | 50 | :param name: the Data name. 51 | :param packet: the raw Data packet. 52 | """ 53 | logging.getLogger(__name__).debug(f'Cache save: {Name.to_str(name)}') 54 | self.data[name] = bytes(packet) 55 | 56 | 57 | class MemoryCachePolicy(policy.Cache): 58 | """ 59 | MemoryCachePolicy stores Data packets in memory. 60 | """ 61 | def __init__(self, cache): 62 | super().__init__() 63 | self.cache = cache 64 | 65 | async def search(self, match: MatchedNode, name: FormalName, param: InterestParam): 66 | return await self.cache.search(name, param) 67 | 68 | async def save(self, match: MatchedNode, name: FormalName, packet: BinaryStr): 69 | await self.cache.save(name, packet) 70 | -------------------------------------------------------------------------------- /src/ndn/schema/util.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from typing import Union, List, Tuple 19 | from ..encoding import Name, Component, BinaryStr 20 | 21 | NamePattern = List[Union[BinaryStr, Tuple[int, int, str]]] 22 | r""" 23 | NamePattern is a list containing mixed name components and varaible patterns. 24 | A variable pattern is a capturing pattern that matches with exactly one name component. 25 | It is a tuple containing 3 variables: 26 | 27 | - The 1st element is reserved and always 0. This is a quick and dirty solution in this PoC implementation 28 | It will be used if we want to support multiple name components matching patterns. 29 | - The 2nd element is the TLV type of the name component to be matched. 30 | - The 3rd element is the name of the pattern variable. 31 | """ 32 | 33 | 34 | def norm_pattern(name: str) -> NamePattern: 35 | """ 36 | This function returns a normalized name pattern from a string, just like normalizing a name. 37 | 38 | :param name: the name pattern string. 39 | :return: normalized name pattern. 40 | """ 41 | ret = Name.normalize(name)[:] 42 | for i, comp in enumerate(ret): 43 | comp_type = Component.get_type(comp) 44 | comp_value = Component.get_value(comp) 45 | if comp_type == Component.TYPE_GENERIC and comp_value[0] == b'<'[0] and comp_value[-1] == b'>'[0]: 46 | content = bytes(comp_value[1:-1]).decode() 47 | eq_sgn = content.find(':') 48 | if eq_sgn >= 0: 49 | type_str = content[:eq_sgn] 50 | if type_str == 'sha256digest': 51 | type_val = Component.TYPE_IMPLICIT_SHA256 52 | elif type_str == 'params-sha256': 53 | type_val = Component.TYPE_PARAMETERS_SHA256 54 | elif type_str in Component.ALTERNATE_URI_STR: 55 | type_val = Component.ALTERNATE_URI_STR[type_str] 56 | else: 57 | type_val = int(type_str) 58 | content = content[eq_sgn+1:] 59 | else: 60 | type_val = Component.TYPE_GENERIC 61 | if not content: 62 | raise ValueError('Pattern variable name cannot be empty') 63 | ret[i] = (0, type_val, content) 64 | return ret 65 | -------------------------------------------------------------------------------- /src/ndn/security/__init__.py: -------------------------------------------------------------------------------- 1 | from .keychain import * 2 | from .signer import * 3 | from .tpm import * 4 | from .validator import * 5 | 6 | 7 | __all__ = [] 8 | __all__.extend(keychain.__all__) 9 | __all__.extend(signer.__all__) 10 | __all__.extend(tpm.__all__) 11 | __all__.extend(validator.__all__) 12 | -------------------------------------------------------------------------------- /src/ndn/security/keychain/__init__.py: -------------------------------------------------------------------------------- 1 | from .keychain import Keychain, AbstractIdentity, AbstractKey, AbstractCertificate 2 | from .keychain_sqlite3 import Identity, Key, Certificate, KeychainSqlite3 3 | from .keychain_digest import KeychainDigest 4 | 5 | __all__ = ['Keychain', 'Identity', 'Key', 'Certificate', 'KeychainSqlite3', 'KeychainDigest', 6 | 'AbstractIdentity', 'AbstractKey', 'AbstractCertificate'] 7 | -------------------------------------------------------------------------------- /src/ndn/security/keychain/keychain_digest.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from .keychain import Keychain 19 | from typing import Dict, Any 20 | from ..signer.sha256_digest_signer import DigestSha256Signer 21 | 22 | 23 | class KeychainDigest(Keychain): 24 | """ 25 | A signer which has no Identity and always returns a SHA-256 digest signer. 26 | """ 27 | def get_signer(self, sign_args: Dict[str, Any]): 28 | if sign_args.get('no_signature', False): 29 | return None 30 | else: 31 | return DigestSha256Signer() 32 | 33 | def __iter__(self): 34 | return None 35 | 36 | def __len__(self) -> int: 37 | return 0 38 | 39 | def __getitem__(self, name): 40 | raise KeyError(name) 41 | -------------------------------------------------------------------------------- /src/ndn/security/signer/__init__.py: -------------------------------------------------------------------------------- 1 | from .sha256_rsa_signer import Sha256WithRsaSigner 2 | from .sha256_digest_signer import DigestSha256Signer 3 | from .sha256_ecdsa_signer import Sha256WithEcdsaSigner 4 | from .sha256_hmac_signer import HmacSha256Signer 5 | from .null_signer import NullSigner 6 | from .ed25519_signer import Ed25519Signer 7 | 8 | 9 | __all__ = ['Sha256WithRsaSigner', 'DigestSha256Signer', 'Sha256WithEcdsaSigner', 'HmacSha256Signer', 10 | 'NullSigner', 'Ed25519Signer'] 11 | -------------------------------------------------------------------------------- /src/ndn/security/signer/ed25519_signer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2022 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from Cryptodome.PublicKey import ECC 19 | from Cryptodome.Hash import SHA512 20 | from Cryptodome.Signature import eddsa 21 | from ...encoding import Signer, SignatureType, KeyLocator, NonStrictName, VarBinaryStr, BinaryStr 22 | 23 | 24 | class Ed25519Signer(Signer): 25 | key_locator_name: NonStrictName 26 | 27 | def __init__(self, key_locator_name: NonStrictName, key_bits: BinaryStr): 28 | """ 29 | Create an Ed25519Signer 30 | 31 | .. note:: 32 | `key_bits` must be DER format. If you have a 32B raw key bits, 33 | prepend it with `b'0.\x02\x01\x000\x05\x06\x03+ep\x04"\x04 '`. 34 | """ 35 | self.key_locator_name = key_locator_name 36 | self.key = ECC.import_key(bytes(key_bits)) 37 | 38 | def write_signature_info(self, signature_info): 39 | signature_info.signature_type = SignatureType.ED25519 40 | signature_info.key_locator = KeyLocator() 41 | signature_info.key_locator.name = self.key_locator_name 42 | 43 | def get_signature_value_size(self): 44 | return 64 45 | 46 | def write_signature_value(self, wire: VarBinaryStr, contents: list[VarBinaryStr]) -> int: 47 | # Copying is needed as cryptography library only support bytes 48 | signer = eddsa.new(self.key, 'rfc8032') 49 | signature = signer.sign(b''.join(contents)) 50 | wire[:] = signature 51 | return len(signature) 52 | -------------------------------------------------------------------------------- /src/ndn/security/signer/null_signer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2021 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from typing import List 19 | from ...encoding import Signer, SignatureType, VarBinaryStr 20 | 21 | 22 | class NullSigner(Signer): 23 | def write_signature_info(self, signature_info): 24 | signature_info.signature_type = SignatureType.NULL 25 | signature_info.key_locator = None 26 | 27 | def get_signature_value_size(self): 28 | return 0 29 | 30 | def write_signature_value(self, wire: VarBinaryStr, contents: List[VarBinaryStr]) -> int: 31 | wire[:] = b'' 32 | return 0 33 | -------------------------------------------------------------------------------- /src/ndn/security/signer/sha256_digest_signer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from typing import List 19 | from Cryptodome.Hash import SHA256 20 | from ...encoding import Signer, SignatureType, VarBinaryStr 21 | from ...utils import timestamp, gen_nonce_64 22 | 23 | 24 | class DigestSha256Signer(Signer): 25 | for_interest: bool 26 | 27 | def __init__(self, for_interest: bool = False): 28 | self.for_interest = for_interest 29 | 30 | def write_signature_info(self, signature_info): 31 | signature_info.signature_type = SignatureType.DIGEST_SHA256 32 | signature_info.key_locator = None 33 | if self.for_interest: 34 | signature_info.signature_time = timestamp() 35 | signature_info.signature_nonce = gen_nonce_64() 36 | 37 | def get_signature_value_size(self): 38 | return 32 39 | 40 | def write_signature_value(self, wire: VarBinaryStr, contents: List[VarBinaryStr]) -> int: 41 | h = SHA256.new() 42 | for blk in contents: 43 | h.update(blk) 44 | wire[:] = h.digest() 45 | return 32 46 | -------------------------------------------------------------------------------- /src/ndn/security/signer/sha256_ecdsa_signer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from typing import List, Union 19 | from Cryptodome.Hash import SHA256 20 | from Cryptodome.PublicKey import ECC 21 | from Cryptodome.Signature import DSS 22 | from ...encoding import Signer, SignatureType, KeyLocator, NonStrictName, VarBinaryStr 23 | 24 | 25 | class Sha256WithEcdsaSigner(Signer): 26 | # SHA256 doesn't work for P-384 and P-521 27 | key_locator_name: NonStrictName 28 | key_der: bytes 29 | curve_bit: int 30 | key_size: int 31 | 32 | def __init__(self, key_locator_name: NonStrictName, key_der: Union[bytes, str]): 33 | self.key_locator_name = key_locator_name 34 | self.key_der = key_der 35 | self.key = ECC.import_key(self.key_der) 36 | curve = self.key.curve 37 | if curve[-2:] == 'r1' or curve[-2:] == 'v1': 38 | self.curve_bit = int(curve[-5:-2]) 39 | else: 40 | self.curve_bit = int(curve[-3:]) 41 | self.key_size = (self.curve_bit * 2 + 7) // 8 42 | self.key_size += self.key_size % 2 43 | 44 | def write_signature_info(self, signature_info): 45 | signature_info.signature_type = SignatureType.SHA256_WITH_ECDSA 46 | signature_info.key_locator = KeyLocator() 47 | signature_info.key_locator.name = self.key_locator_name 48 | 49 | def get_signature_value_size(self): 50 | return self.key_size + 8 51 | 52 | def write_signature_value(self, wire: VarBinaryStr, contents: List[VarBinaryStr]) -> int: 53 | h = SHA256.new() 54 | for blk in contents: 55 | h.update(blk) 56 | signature = DSS.new(self.key, 'fips-186-3', 'der').sign(h) 57 | real_len = len(signature) 58 | wire[:real_len] = signature 59 | return real_len 60 | -------------------------------------------------------------------------------- /src/ndn/security/signer/sha256_hmac_signer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from typing import List 19 | from Cryptodome.Hash import SHA256, HMAC 20 | from ...encoding import Signer, SignatureType, KeyLocator, NonStrictName, VarBinaryStr 21 | 22 | 23 | class HmacSha256Signer(Signer): 24 | key_locator_name: NonStrictName 25 | key_bytes: bytes 26 | 27 | def __init__(self, key_locator_name: NonStrictName, key_bytes: bytes): 28 | self.key_locator_name = key_locator_name 29 | self.key_bytes = key_bytes 30 | 31 | def write_signature_info(self, signature_info): 32 | signature_info.signature_type = SignatureType.HMAC_WITH_SHA256 33 | signature_info.key_locator = KeyLocator() 34 | signature_info.key_locator.name = self.key_locator_name 35 | 36 | def get_signature_value_size(self): 37 | return 32 38 | 39 | def write_signature_value(self, wire: VarBinaryStr, contents: List[VarBinaryStr]) -> int: 40 | h = HMAC.new(self.key_bytes, digestmod=SHA256) 41 | for blk in contents: 42 | h.update(blk) 43 | wire[:] = h.digest() 44 | return 32 45 | -------------------------------------------------------------------------------- /src/ndn/security/signer/sha256_rsa_signer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from typing import List, Union 19 | from Cryptodome.Hash import SHA256 20 | from Cryptodome.PublicKey import RSA 21 | from Cryptodome.Signature import pkcs1_15 22 | from ...encoding import Signer, SignatureType, KeyLocator, NonStrictName, VarBinaryStr 23 | 24 | 25 | class Sha256WithRsaSigner(Signer): 26 | key_locator_name: NonStrictName 27 | key_der: bytes 28 | 29 | def __init__(self, key_locator_name: NonStrictName, key_der: Union[str, bytes]): 30 | self.key_locator_name = key_locator_name 31 | self.key_der = key_der 32 | self.key = RSA.import_key(self.key_der) 33 | 34 | def write_signature_info(self, signature_info): 35 | signature_info.signature_type = SignatureType.SHA256_WITH_RSA 36 | signature_info.key_locator = KeyLocator() 37 | signature_info.key_locator.name = self.key_locator_name 38 | 39 | def get_signature_value_size(self): 40 | return self.key.size_in_bytes() 41 | 42 | def write_signature_value(self, wire: VarBinaryStr, contents: List[VarBinaryStr]) -> int: 43 | h = SHA256.new() 44 | for blk in contents: 45 | h.update(blk) 46 | signature = pkcs1_15.new(self.key).sign(h) 47 | wire[:] = signature 48 | return len(signature) 49 | -------------------------------------------------------------------------------- /src/ndn/security/tpm/__init__.py: -------------------------------------------------------------------------------- 1 | from .tpm import Tpm 2 | from .tpm_file import TpmFile 3 | 4 | 5 | __all__ = ['Tpm', 'TpmFile'] 6 | -------------------------------------------------------------------------------- /src/ndn/security/tpm/tpm.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import abc 19 | from typing import Tuple, Optional 20 | from Cryptodome.Hash import SHA256 21 | from Cryptodome.Random import get_random_bytes 22 | from ...app_support.security_v2 import KEY_COMPONENT 23 | from ...encoding import Signer, NonStrictName, BinaryStr, FormalName, Component 24 | 25 | 26 | class Tpm(metaclass=abc.ABCMeta): 27 | @abc.abstractmethod 28 | def get_signer(self, key_name: NonStrictName, key_locator_name: Optional[NonStrictName] = None) -> Signer: 29 | pass 30 | 31 | @abc.abstractmethod 32 | def generate_key(self, id_name: FormalName, key_type: str = 'rsa', **kwargs) -> Tuple[FormalName, BinaryStr]: 33 | pass 34 | 35 | @abc.abstractmethod 36 | def key_exist(self, key_name: FormalName) -> bool: 37 | pass 38 | 39 | @abc.abstractmethod 40 | def delete_key(self, key_name: FormalName): 41 | pass 42 | 43 | def construct_key_name(self, id_name: FormalName, pub_key: BinaryStr, **kwargs) -> FormalName: 44 | key_id = kwargs.get('key_id', None) 45 | key_id_type = kwargs.get('key_id_type', 'random') 46 | if not key_id: 47 | if key_id_type == 'random': 48 | while True: 49 | key_id = Component.from_bytes(get_random_bytes(8)) 50 | if not self.key_exist(id_name + [KEY_COMPONENT, key_id]): 51 | break 52 | elif key_id_type == 'sha256': 53 | h = SHA256.new() 54 | h.update(pub_key) 55 | key_id = Component.from_bytes(h.digest()) 56 | else: 57 | raise ValueError(f'KeyIdType not supported: {key_id_type}') 58 | elif isinstance(key_id, str): 59 | key_id = Component.from_str(key_id) 60 | return id_name + [KEY_COMPONENT, key_id] 61 | -------------------------------------------------------------------------------- /src/ndn/security/validator/__init__.py: -------------------------------------------------------------------------------- 1 | from .digest_validator import sha256_digest_checker, params_sha256_checker, union_checker 2 | from .known_key_validator import verify_rsa, verify_ecdsa, verify_hmac, \ 3 | EccChecker, RsaChecker, HmacChecker, verify_ed25519, Ed25519Checker 4 | 5 | 6 | __all__ = ['sha256_digest_checker', 'params_sha256_checker', 'union_checker', 7 | 'verify_ecdsa', 'verify_rsa', 'verify_hmac', 8 | 'EccChecker', 'RsaChecker', 'HmacChecker', 9 | 'verify_ed25519', 'Ed25519Checker'] 10 | -------------------------------------------------------------------------------- /src/ndn/security/validator/digest_validator.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import logging 19 | from hashlib import sha256 20 | from ...encoding import FormalName, SignatureType, Name, SignaturePtrs 21 | from ...types import Validator 22 | 23 | 24 | async def sha256_digest_checker(name: FormalName, sig: SignaturePtrs) -> bool: 25 | sig_info = sig.signature_info 26 | covered_part = sig.signature_covered_part 27 | sig_value = sig.signature_value_buf 28 | if sig_info and sig_info.signature_type == SignatureType.DIGEST_SHA256: 29 | sha256_algo = sha256() 30 | if not covered_part or not sig_value: 31 | ret = False 32 | else: 33 | for blk in covered_part: 34 | sha256_algo.update(blk) 35 | ret = sha256_algo.digest() == sig_value 36 | logging.getLogger(__name__).debug('Digest check %s -> %s' % (Name.to_str(name), ret)) 37 | return ret 38 | else: 39 | return True 40 | 41 | 42 | # This is automatically called 43 | async def params_sha256_checker(name: FormalName, sig: SignaturePtrs) -> bool: 44 | covered_part = sig.digest_covered_part 45 | sig_value = sig.digest_value_buf 46 | sha256_algo = sha256() 47 | if not covered_part or not sig_value: 48 | ret = False 49 | else: 50 | for blk in covered_part: 51 | sha256_algo.update(blk) 52 | ret = sha256_algo.digest() == sig_value 53 | logging.getLogger(__name__).debug('Interest params-sha256 check %s -> %s' % (Name.to_str(name), ret)) 54 | return ret 55 | 56 | 57 | def union_checker(*args) -> Validator: 58 | async def wrapper(name: FormalName, sig: SignaturePtrs) -> bool: 59 | for checker in args: 60 | if not await checker(name, sig): 61 | return False 62 | return True 63 | return wrapper 64 | -------------------------------------------------------------------------------- /src/ndn/transport/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/python-ndn/440f65d092a544f5e5571cc71b7f4861c702a3f7/src/ndn/transport/__init__.py -------------------------------------------------------------------------------- /src/ndn/transport/dummy_face.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import asyncio as aio 19 | 20 | from ndn.encoding import parse_tl_num 21 | from ndn.transport.face import Face 22 | 23 | 24 | class DummyFace(Face): 25 | app = None 26 | 27 | def __init__(self, test_func): 28 | super().__init__() 29 | self.output_buf = b'' 30 | self.test_func = test_func 31 | self.event = aio.Event() 32 | self.expected_len = 2 ** 32 33 | 34 | async def open(self): 35 | self.running = True 36 | 37 | def shutdown(self): 38 | self.running = False 39 | 40 | def send(self, data: bytes): 41 | self.output_buf += data 42 | if len(self.output_buf) >= self.expected_len: 43 | self.event.set() 44 | 45 | async def run(self): 46 | await self.test_func(self) 47 | if self.app: 48 | self.app.shutdown() 49 | 50 | def isLocalFace(self): 51 | return True 52 | 53 | async def consume_output(self, expected_output, timeout=0.01): 54 | self.expected_len = len(expected_output) 55 | if len(self.output_buf) < self.expected_len: 56 | await aio.wait_for(self.event.wait(), timeout) 57 | self.expected_len = 2 ** 32 58 | self.event.clear() 59 | assert self.output_buf == expected_output 60 | self.output_buf = b'' 61 | 62 | async def ignore_output(self, length, timeout=0.1): 63 | self.expected_len = length 64 | await aio.wait_for(self.event.wait(), timeout) 65 | self.expected_len = 2 ** 32 66 | self.event.clear() 67 | self.output_buf = b'' 68 | 69 | async def input_packet(self, packet): 70 | packet = memoryview(packet) 71 | typ, typ_len = parse_tl_num(packet) 72 | siz, siz_len = parse_tl_num(packet, typ_len) 73 | offset = typ_len + siz_len 74 | assert len(packet) == offset + siz 75 | await self.callback(typ, packet) 76 | -------------------------------------------------------------------------------- /src/ndn/transport/face.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import abc 19 | from typing import Any, Callable, Coroutine 20 | 21 | 22 | class Face(metaclass=abc.ABCMeta): 23 | running: bool = False 24 | callback: Callable[[int, bytes], Coroutine[Any, None, None]] = None 25 | 26 | def __init__(self): 27 | self.running = False 28 | 29 | @abc.abstractmethod 30 | async def open(self): 31 | pass 32 | 33 | # TODO: Should switch to async function, since some requires gracefully shutdown 34 | @abc.abstractmethod 35 | def shutdown(self): 36 | pass 37 | 38 | @abc.abstractmethod 39 | def send(self, data: bytes): 40 | pass 41 | 42 | @abc.abstractmethod 43 | async def run(self): 44 | pass 45 | 46 | @abc.abstractmethod 47 | async def isLocalFace(self): 48 | pass 49 | -------------------------------------------------------------------------------- /src/ndn/transport/ip_face.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from socket import AF_INET, AF_INET6, gaierror, getaddrinfo 19 | 20 | from .face import Face 21 | 22 | 23 | class IpFace(Face): 24 | 25 | def isLocalFace(self): 26 | local_checker = {AF_INET: lambda x: x.startswith('127'), 27 | AF_INET6: lambda x: x == '::1'} 28 | try: 29 | r = getaddrinfo(self.host, self.port) 30 | except gaierror: 31 | return False 32 | for res_family, _, _, _, addr in r: 33 | if not local_checker[res_family](addr[0]): 34 | return False 35 | return True 36 | -------------------------------------------------------------------------------- /src/ndn/transport/prefix_registerer.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2022 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import abc 19 | from .. import encoding as enc 20 | 21 | 22 | class PrefixRegisterer(abc.ABC): 23 | def __init__(self): 24 | self.app = None 25 | 26 | def set_app(self, app): 27 | self.app = app 28 | 29 | @abc.abstractmethod 30 | async def register(self, name: enc.NonStrictName) -> bool: 31 | pass 32 | 33 | @abc.abstractmethod 34 | async def unregister(self, name: enc.NonStrictName) -> bool: 35 | pass 36 | -------------------------------------------------------------------------------- /src/ndn/transport/stream_face.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import abc 19 | import asyncio as aio 20 | import io 21 | from typing import Optional 22 | 23 | from ndn.transport.ip_face import IpFace 24 | 25 | from ..encoding.tlv_var import read_tl_num_from_stream 26 | from ..platform import Platform 27 | from .face import Face 28 | 29 | 30 | class StreamFace(Face, metaclass=abc.ABCMeta): 31 | reader: Optional[aio.StreamReader] = None 32 | writer: Optional[aio.StreamWriter] = None 33 | 34 | def shutdown(self): 35 | self.running = False 36 | if self.writer: 37 | self.writer.close() 38 | self.writer = None 39 | 40 | async def run(self): 41 | while self.running: 42 | try: 43 | bio = io.BytesIO() 44 | typ = await read_tl_num_from_stream(self.reader, bio) 45 | siz = await read_tl_num_from_stream(self.reader, bio) 46 | bio.write(await self.reader.readexactly(siz)) 47 | buf = bio.getvalue() 48 | aio.create_task(self.callback(typ, buf)) 49 | except (aio.IncompleteReadError, ConnectionResetError): 50 | self.shutdown() 51 | 52 | def send(self, data: bytes): 53 | self.writer.write(data) 54 | 55 | 56 | class UnixFace(StreamFace): 57 | path: str = '/run/nfd.sock' 58 | 59 | def __init__(self, path: str = ''): 60 | super().__init__() 61 | if path: 62 | self.path = path 63 | 64 | async def open(self): 65 | self.reader, self.writer = await Platform().open_unix_connection(self.path) 66 | self.running = True 67 | 68 | def isLocalFace(self): 69 | return True 70 | 71 | 72 | class TcpFace(StreamFace, IpFace): 73 | host: str = '127.0.0.1' 74 | port: int = 6363 75 | 76 | def __init__(self, host: str = '', port: int = 0): 77 | super().__init__() 78 | if host: 79 | self.host = host 80 | if port: 81 | self.port = port 82 | 83 | async def open(self): 84 | self.reader, self.writer = await aio.open_connection(self.host, self.port) 85 | self.running = True 86 | -------------------------------------------------------------------------------- /src/ndn/transport/udp_face.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import asyncio as aio 19 | import logging 20 | from typing import Tuple 21 | 22 | from ..encoding.tlv_var import parse_tl_num 23 | from .ip_face import IpFace 24 | 25 | 26 | class UdpFace(IpFace): 27 | 28 | def __init__(self, host: str = '127.0.0.1', port: int = 6363): 29 | super().__init__() 30 | self.host = host 31 | self.port = port 32 | 33 | async def open(self): 34 | 35 | class PacketHandler: 36 | 37 | def __init__(self, callback, close) -> None: 38 | self.callback = callback 39 | self.close = close 40 | 41 | def connection_made( 42 | self, transport: aio.DatagramTransport) -> None: 43 | self.transport = transport 44 | 45 | def datagram_received( 46 | self, data: bytes, addr: Tuple[str, int]) -> None: 47 | typ, _ = parse_tl_num(data) 48 | aio.create_task(self.callback(typ, data)) 49 | return 50 | 51 | def send(self, data): 52 | self.transport.sendto(data) 53 | 54 | def error_received(self, exc: Exception) -> None: 55 | self.close.set_result(True) 56 | logging.getLogger(__name__).warning(exc) 57 | 58 | def connection_lost(self, exc): 59 | if not self.close.done(): 60 | self.close.set_result(True) 61 | if exc: 62 | logging.getLogger(__name__).warning(exc) 63 | 64 | loop = aio.get_running_loop() 65 | self.running = True 66 | close = loop.create_future() 67 | handler = PacketHandler(self.callback, close) 68 | transport, _ = await loop.create_datagram_endpoint( 69 | lambda: handler, 70 | remote_addr=(self.host, self.port)) 71 | self.handler = handler 72 | self.transport = transport 73 | self.close = close 74 | 75 | async def run(self): 76 | await self.close 77 | 78 | def send(self, data: bytes): 79 | self.handler.send(data) 80 | 81 | def shutdown(self): 82 | self.running = False 83 | self.transport.close() 84 | -------------------------------------------------------------------------------- /src/ndn/utils.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import time 19 | from random import randint 20 | 21 | 22 | def timestamp(): 23 | """ 24 | Generate a timestamp number. 25 | 26 | :return: the time in milliseconds since the epoch as an integer 27 | """ 28 | return int(time.time() * 1000) 29 | 30 | 31 | def gen_nonce(): 32 | """ 33 | Generate a random nonce. 34 | 35 | :return: a random 32-bit unsigned integer. 36 | """ 37 | return randint(1, 2 ** 32 - 1) 38 | 39 | 40 | def gen_nonce_64(): 41 | """ 42 | Generate a random 64-bit nonce. 43 | 44 | :return: a random 64-bit unsigned integer. 45 | """ 46 | return randint(1, 2 ** 64 - 1) 47 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/named-data/python-ndn/440f65d092a544f5e5571cc71b7f4861c702a3f7/tests/__init__.py -------------------------------------------------------------------------------- /tests/ecies_test.py: -------------------------------------------------------------------------------- 1 | from Cryptodome.PublicKey import ECC 2 | from Cryptodome.Random import get_random_bytes 3 | from ndn.app_support.ecies import encrypt, decrypt 4 | 5 | 6 | class TestEcies: 7 | def test_r256(self): 8 | priv_key = ECC.generate(curve='secp256r1') 9 | data = get_random_bytes(100) 10 | pub_key = priv_key.public_key() 11 | cipher_text = encrypt(pub_key, data) 12 | plain_text = decrypt(priv_key, cipher_text) 13 | assert plain_text == data 14 | 15 | def test_ed25519(self): 16 | priv_key = ECC.generate(curve='ed25519') 17 | data = get_random_bytes(100) 18 | pub_key = priv_key.public_key() 19 | cipher_text = encrypt(pub_key, data) 20 | plain_text = decrypt(priv_key, cipher_text) 21 | assert plain_text == data 22 | -------------------------------------------------------------------------------- /tests/encoding/ndnlp_v2_test.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from ndn.encoding import parse_network_nack, parse_interest, make_network_nack, make_interest, \ 19 | NackReason, Name, InterestParam 20 | 21 | 22 | class TestNetworkNack: 23 | @staticmethod 24 | def test1(): 25 | lp_packet = (b"\x64\x32\xfd\x03\x20\x05\xfd\x03\x21\x01\x96" 26 | b"\x50\x27\x05\x25\x07\x1f\x08\tlocalhost\x08\x03nfd\x08\x05faces\x08\x06events" 27 | b"\x21\x00\x12\x00") 28 | nack_reason, interest = parse_network_nack(lp_packet, True) 29 | assert nack_reason == NackReason.NO_ROUTE 30 | name, param, _, _ = parse_interest(interest) 31 | assert name == Name.from_str("/localhost/nfd/faces/events") 32 | assert param.must_be_fresh 33 | assert param.can_be_prefix 34 | 35 | @staticmethod 36 | def test2(): 37 | interest = make_interest('/localhost/nfd/faces/events', 38 | InterestParam(must_be_fresh=True, can_be_prefix=True)) 39 | lp_packet = make_network_nack(interest, NackReason.NO_ROUTE) 40 | assert lp_packet == (b"\x64\x36\xfd\x03\x20\x05\xfd\x03\x21\x01\x96" 41 | b"\x50\x2b\x05\x29\x07\x1f\x08\tlocalhost\x08\x03nfd\x08\x05faces\x08\x06events" 42 | b"\x21\x00\x12\x00\x0c\x02\x0f\xa0") 43 | -------------------------------------------------------------------------------- /tests/face_test.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import asyncio 19 | 20 | from ndn.client_conf import default_face 21 | from ndn.transport.stream_face import TcpFace, UnixFace 22 | from ndn.transport.udp_face import UdpFace 23 | 24 | 25 | class TestFace: 26 | def test(self): 27 | url = 'unix:///tmp/nfd.sock' 28 | face = default_face(url) 29 | assert isinstance(face, UnixFace) 30 | assert face.path == '/tmp/nfd.sock' 31 | 32 | url = 'tcp://localhost' 33 | face = default_face(url) 34 | assert isinstance(face, TcpFace) 35 | assert face.host == 'localhost' 36 | assert face.port == 6363 37 | 38 | url = 'tcp4://localhost:6364' 39 | face = default_face(url) 40 | assert isinstance(face, TcpFace) 41 | assert face.host == 'localhost' 42 | assert face.port == 6364 43 | 44 | url = 'tcp6://localhost' 45 | face = default_face(url) 46 | assert isinstance(face, TcpFace) 47 | assert face.host == 'localhost' 48 | assert face.port == 6363 49 | 50 | url = 'udp://localhost' 51 | face = default_face(url) 52 | assert isinstance(face, UdpFace) 53 | assert face.host == 'localhost' 54 | assert face.port == 6363 55 | 56 | url = 'udp4://localhost:6364' 57 | face = default_face(url) 58 | assert isinstance(face, UdpFace) 59 | assert face.host == 'localhost' 60 | assert face.port == 6364 61 | 62 | url = 'udp6://localhost' 63 | face = default_face(url) 64 | assert isinstance(face, UdpFace) 65 | assert face.host == 'localhost' 66 | assert face.port == 6363 67 | 68 | url = 'udp6://[::1]:6465' 69 | face = default_face(url) 70 | assert isinstance(face, UdpFace) 71 | assert face.host == '::1' 72 | assert face.port == 6465 73 | 74 | def test_connection(self): 75 | url = 'udp://localhost' 76 | face = default_face(url) 77 | asyncio.run(face.open()) 78 | 79 | url = 'udp4://localhost:6364' 80 | face = default_face(url) 81 | asyncio.run(face.open()) 82 | 83 | url = 'udp6://localhost' 84 | face = default_face(url) 85 | asyncio.run(face.open()) 86 | 87 | url = 'udp6://[::1]:6465' 88 | face = default_face(url) 89 | asyncio.run(face.open()) 90 | -------------------------------------------------------------------------------- /tests/integration/keychain_test.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2020 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | import os 19 | from tempfile import TemporaryDirectory 20 | from Cryptodome.Hash import SHA256 21 | from Cryptodome.PublicKey import ECC 22 | from Cryptodome.Signature import DSS 23 | from ndn.encoding import make_data, MetaInfo, parse_data, Name 24 | from ndn.security import KeychainSqlite3, TpmFile 25 | 26 | 27 | class TestKeychainSqlite3: 28 | tpm_dir: str 29 | pib_file: str 30 | keychain: KeychainSqlite3 31 | tpm: TpmFile 32 | pub_key: bytes 33 | cert: bytes 34 | 35 | def test_main(self): 36 | with TemporaryDirectory() as tmpdirname: 37 | self.prepare_db(tmpdirname) 38 | self.tpm = TpmFile(self.tpm_dir) 39 | self.keychain = KeychainSqlite3(self.pib_file, self.tpm) 40 | 41 | self.create_key() 42 | self.verify_data() 43 | self.verify_cert() 44 | 45 | self.keychain.del_identity('test') 46 | assert len(self.keychain) == 0 47 | self.keychain.shutdown() 48 | 49 | def prepare_db(self, base_dir): 50 | self.pib_file = os.path.join(base_dir, 'pib.db') 51 | self.tpm_dir = os.path.join(base_dir, 'ndnsec-key-file') 52 | KeychainSqlite3.initialize(self.pib_file, 'tpm-file', self.tpm_dir) 53 | 54 | def create_key(self): 55 | self.keychain.touch_identity('test') 56 | ident = self.keychain.default_identity() 57 | assert ident.name == Name.from_str('test') 58 | self.pub_key = ident.default_key().key_bits 59 | self.cert = ident.default_key().default_cert().data 60 | 61 | def verify(self, pkt): 62 | _, _, _, sig_ptrs = parse_data(pkt) 63 | pub_key = ECC.import_key(self.pub_key) 64 | verifier = DSS.new(pub_key, 'fips-186-3', 'der') 65 | h = SHA256.new() 66 | for content in sig_ptrs.signature_covered_part: 67 | h.update(content) 68 | verifier.verify(h, bytes(sig_ptrs.signature_value_buf)) 69 | return sig_ptrs.signature_info.key_locator.name 70 | 71 | def verify_data(self): 72 | signer = self.keychain.get_signer({}) 73 | data = make_data('/test/data', MetaInfo(), b'content', signer=signer) 74 | key_locator_name = self.verify(data) 75 | assert (Name.normalize(key_locator_name) == 76 | Name.normalize(self.keychain.default_identity().default_key().default_cert().name)) 77 | 78 | def verify_cert(self): 79 | key_locator_name = self.verify(self.cert) 80 | assert key_locator_name == self.keychain.default_identity().default_key().name 81 | -------------------------------------------------------------------------------- /tests/misc/boost_info_test.py: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------------------------------------- 2 | # Copyright (C) 2019-2022 The python-ndn authors 3 | # 4 | # This file is part of python-ndn. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # ----------------------------------------------------------------------------- 18 | from ndn.contrib.boost_info import PropertyTree 19 | 20 | 21 | class TestPropertyTree: 22 | @staticmethod 23 | def test_basic(): 24 | text = r''' 25 | key1 value1 26 | key2 27 | { 28 | key3 value3 29 | { 30 | key4 "value4 with spaces" 31 | } 32 | key5 value5 33 | } 34 | ''' 35 | tree = PropertyTree.parse(text) 36 | root = tree.root 37 | assert len(root['key1']) == 1 38 | assert root['key1'][0].value == 'value1' 39 | assert len(root['key2']) == 1 40 | k2 = root['key2'][0] 41 | assert k2.value == '' 42 | assert k2.get('key3.key4') == 'value4 with spaces' 43 | assert k2.get_node('key5').value == 'value5' 44 | 45 | @staticmethod 46 | def test_full(): 47 | text = r''' 48 | ; A comment 49 | key1 value1 ; Another comment 50 | key2 51 | { 52 | subkey "value split "\ 53 | "over three"\ 54 | "lines" 55 | { 56 | a_key_without_value "" 57 | "" value ; Empty key with a value 58 | "" "" ; Empty key with empty value! 59 | } 60 | } 61 | ''' 62 | tree = PropertyTree.parse(text) 63 | root = tree.root 64 | assert len(root.children) == 2 65 | assert root.get('key1') == 'value1' 66 | # Special characters are not recognized 67 | assert root.get('key2.subkey') == 'value split over threelines' 68 | assert root.get('key2.subkey.a_key_without_value') == '' 69 | lst = root.get_node('key2.subkey')[''] 70 | assert len(lst) == 2 71 | assert lst[0].value == 'value' 72 | assert lst[1].value == '' 73 | --------------------------------------------------------------------------------