├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md ├── python_matcher.json └── workflows │ ├── bloat_formatter.sh │ ├── cargo_audit.yml │ ├── cargo_bloat.yml │ ├── ci.yml │ ├── cifuzz.yml │ ├── coveralls.yml │ ├── mdlint.yml │ └── reproducible.yml ├── .gitignore ├── .gitmodules ├── .markdownlint.json ├── .pylintrc ├── CITATION.cff ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── OpenSK.code-workspace ├── README.md ├── SECURITY.md ├── boards └── nordic │ ├── nrf52840_dongle_dfu │ ├── Cargo.toml │ ├── Makefile │ ├── build.rs │ └── layout.ld │ ├── nrf52840_dongle_opensk │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── jtag │ │ ├── gdbinit_pca10040.jlink │ │ └── jdbserver_pca10040.sh │ ├── layout.ld │ └── src │ │ ├── io.rs │ │ └── main.rs │ ├── nrf52840_mdk_dfu │ ├── Cargo.toml │ ├── Makefile │ ├── build.rs │ ├── layout.ld │ └── src │ │ ├── io.rs │ │ └── main.rs │ ├── nrf52840dk_opensk │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── jtag │ │ ├── gdbinit_pca10040.jlink │ │ └── jdbserver_pca10040.sh │ ├── layout.ld │ └── src │ │ ├── io.rs │ │ └── main.rs │ ├── nrf52840dk_opensk_a │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ └── layout.ld │ └── nrf52840dk_opensk_b │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ └── layout.ld ├── bootloader ├── .cargo │ └── config.toml ├── Cargo.lock ├── Cargo.toml ├── README.md ├── build.rs ├── memory.x └── src │ ├── bitfields.rs │ ├── crypto_cell.rs │ ├── main.rs │ ├── registers.rs │ └── static_ref.rs ├── build.rs ├── deploy.py ├── docs ├── FIDO2 Certificate Google FIDO20020210209001.pdf ├── boards │ ├── nrf52840_dongle.md │ ├── nrf52840_feitian.md │ ├── nrf52840_mdk.md │ └── nrf52840dk.md ├── contributing.md ├── customization.md ├── debugging.md ├── img │ ├── FIDO2_Certified_L1.png │ ├── OpenSK.svg │ ├── devkit_annotated.jpg │ ├── dongle_clip.jpg │ ├── dongle_front.jpg │ ├── dongle_pads.jpg │ └── enclosure.jpg └── install.md ├── examples ├── console_test.rs ├── crypto_bench.rs ├── erase_storage.rs ├── nfct_test.rs ├── oom_test.rs ├── panic_test.rs └── store_latency.rs ├── fuzzing_setup.sh ├── libraries ├── cbor │ ├── Cargo.lock │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ ├── examples │ │ └── cbor.rs │ ├── fuzz │ │ ├── .gitignore │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── fuzz_targets │ │ │ └── fuzz_target_cbor.rs │ └── src │ │ ├── lib.rs │ │ ├── macros.rs │ │ ├── reader.rs │ │ ├── values.rs │ │ └── writer.rs ├── opensk │ ├── Cargo.lock │ ├── Cargo.toml │ ├── fuzz │ │ ├── .gitignore │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── ctap2_commands_parameters_corpus.json │ │ ├── fuzz_helper │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ └── lib.rs │ │ ├── fuzz_targets │ │ │ ├── fuzz_target_process_ctap1.rs │ │ │ ├── fuzz_target_process_ctap2_client_pin.rs │ │ │ ├── fuzz_target_process_ctap2_client_pin_structured.rs │ │ │ ├── fuzz_target_process_ctap2_get_assertion.rs │ │ │ ├── fuzz_target_process_ctap2_get_assertion_structured.rs │ │ │ ├── fuzz_target_process_ctap2_make_credential.rs │ │ │ ├── fuzz_target_process_ctap2_make_credential_structured.rs │ │ │ ├── fuzz_target_process_ctap_command.rs │ │ │ └── fuzz_target_split_assemble.rs │ │ └── make_corpus.py │ └── src │ │ ├── api │ │ ├── clock.rs │ │ ├── connection.rs │ │ ├── crypto │ │ │ ├── aes256.rs │ │ │ ├── ecdh.rs │ │ │ ├── ecdsa.rs │ │ │ ├── hkdf256.rs │ │ │ ├── hmac256.rs │ │ │ ├── mod.rs │ │ │ ├── rust_crypto.rs │ │ │ └── sha256.rs │ │ ├── customization.rs │ │ ├── firmware_protection.rs │ │ ├── key_store.rs │ │ ├── mod.rs │ │ ├── persist.rs │ │ ├── persist │ │ │ └── keys.rs │ │ ├── private_key.rs │ │ ├── rng.rs │ │ └── user_presence.rs │ │ ├── ctap │ │ ├── apdu.rs │ │ ├── client_pin.rs │ │ ├── command.rs │ │ ├── config_command.rs │ │ ├── credential_management.rs │ │ ├── crypto_wrapper.rs │ │ ├── ctap1.rs │ │ ├── data_formats.rs │ │ ├── hid │ │ │ ├── mod.rs │ │ │ ├── receive.rs │ │ │ └── send.rs │ │ ├── large_blobs.rs │ │ ├── main_hid.rs │ │ ├── mod.rs │ │ ├── pin_protocol.rs │ │ ├── response.rs │ │ ├── secret.rs │ │ ├── status_code.rs │ │ ├── storage.rs │ │ ├── token_state.rs │ │ ├── u2f_up.rs │ │ └── vendor_hid.rs │ │ ├── env │ │ ├── mod.rs │ │ └── test │ │ │ ├── customization.rs │ │ │ └── mod.rs │ │ ├── lib.rs │ │ └── test_helpers │ │ └── mod.rs └── persistent_store │ ├── Cargo.lock │ ├── Cargo.toml │ ├── fuzz │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── examples │ │ └── store.rs │ ├── fuzz_targets │ │ └── store.rs │ └── src │ │ ├── histogram.rs │ │ ├── lib.rs │ │ ├── stats.rs │ │ └── store.rs │ ├── src │ ├── buffer.rs │ ├── concat.rs │ ├── driver.rs │ ├── file.rs │ ├── format.rs │ ├── format │ │ └── bitfield.rs │ ├── fragment.rs │ ├── lib.rs │ ├── linear.rs │ ├── model.rs │ ├── storage.rs │ ├── store.rs │ └── test.rs │ └── tests │ └── store.rs ├── libtock_layout.ld ├── maintainers ├── patches ├── reproduce_board.sh ├── reproduce_hashes.sh └── update_hashes.sh ├── metadata └── metadata.json ├── nrf52840_layout.ld ├── nrf52840_layout_a.ld ├── nrf52840_layout_b.ld ├── patches ├── libtock-rs │ └── 01-alloc-init-feature.patch └── tock │ ├── 01-nrf52-opensk-boards.patch │ ├── 02-remove-ctap-modules.patch │ ├── 03-add-ctap-modules.patch │ ├── 04-vendor-hid.patch │ ├── 05-kernel-utility-method.patch │ ├── 06-persistent-storage.patch │ └── 07-update-uicr.patch ├── reproducible ├── reference_binaries_macos-10.15.sha256sum ├── reference_binaries_ubuntu-18.04.sha256sum ├── reference_elf2tab_macos-10.15.txt ├── reference_elf2tab_ubuntu-18.04.txt └── sample_crypto_data │ ├── aaguid.txt │ ├── opensk.key │ └── opensk_cert.pem ├── requirements.txt ├── reset.sh ├── rules.d └── 55-opensk.rules ├── run_desktop_tests.sh ├── rust-toolchain.toml ├── rustfmt.toml ├── setup-submodules.sh ├── setup.sh ├── src ├── env │ ├── mod.rs │ └── tock │ │ ├── buffer_upgrade_storage.rs │ │ ├── clock.rs │ │ ├── commands.rs │ │ ├── mod.rs │ │ ├── phantom_buffer_storage.rs │ │ ├── storage.rs │ │ ├── storage_helper.rs │ │ └── upgrade_helper.rs ├── lib.rs └── main.rs ├── third_party ├── lang-items │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ └── src │ │ ├── allocator.rs │ │ ├── lib.rs │ │ ├── panic_handler.rs │ │ └── util.rs └── libtock-drivers │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ └── src │ ├── crp.rs │ ├── lib.rs │ ├── nfc.rs │ ├── result.rs │ ├── rng.rs │ ├── storage.rs │ ├── timer.rs │ ├── usb_ctap_hid.rs │ └── util.rs └── tools ├── authenticator_config.py ├── configure.py ├── deploy_partition.py ├── gen_key_materials.sh ├── openssl ├── opensk.conf ├── root-ca.conf └── signing-ca.conf └── vendor_hid_test.py /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected Behavior 2 | 3 | 4 | ## Actual Behavior 5 | 6 | 7 | ## Steps to Reproduce the Problem 8 | 9 | 1. 10 | 1. 11 | 1. 12 | 13 | ## Specifications 14 | 15 | - Version: 16 | - Platform: -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | > It's a good idea to open an issue first for discussion. 4 | 5 | - [ ] Local tests pass (running `run_desktop_tests.sh`) 6 | - [ ] Tested against boards 7 | - [ ] Nordic nRF52840 DK 8 | - [ ] Nordic nRF52840 Dongle (JTAG programmed) 9 | - [ ] Nordic nRF52840 Dongle (DFU programmed) 10 | - [ ] Makerdiary nRF52840 MDK USB Dongle 11 | - [ ] Appropriate changes to README are included in PR 12 | -------------------------------------------------------------------------------- /.github/python_matcher.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "yapf-diff", 5 | "pattern": [ 6 | { 7 | "regexp": "^---\\s*([^\\s]*)\\s*\\(original\\)$", 8 | "file": 1 9 | }, 10 | { 11 | "regexp": "^\\+\\+\\+\\s*([^\\s]*)\\s*\\((.*)\\)$", 12 | "message": 2 13 | }, 14 | { 15 | "regexp": "^@@\\s*-(\\d+),(\\d+)\\s*\\+(\\d+),(\\d+)\\s*@@$", 16 | "line": 1 17 | } 18 | ] 19 | }, 20 | { 21 | "owner": "pylint", 22 | "pattern": [ 23 | { 24 | "regexp": "^PYLINT:(.*)/.*$", 25 | "fromPath": 1 26 | }, 27 | { 28 | "regexp": "^\\*{13}\\s*Module\\s+(.*)$", 29 | "file": 1 30 | }, 31 | { 32 | "regexp": "^([CEFIRW]\\d{4}):\\s*(\\d+)\\s*:\\s*(.*)$", 33 | "code": 1, 34 | "line": 2, 35 | "message": 3 36 | } 37 | ] 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/bloat_formatter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2022 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | cd "$(dirname "$0")" 17 | 18 | # New output file is $1 19 | # Old output file is $2 20 | TMP=comment.md 21 | WARNING="Note: numbers above are a result of guesswork. They are not 100% correct and never will be." 22 | NEW_SIZE=$(cat "$1" | sed -nr 's/.*100.0% (.*)KiB .text.*/\1/p') 23 | OLD_SIZE=$(cat "$2" | sed -nr 's/.*100.0% (.*)KiB .text.*/\1/p') 24 | 25 | echo " 26 | OLD $OLD_SIZE kiB 27 | NEW $NEW_SIZE kiB" > "$TMP" 28 | 29 | echo " 30 | Output of cargo bloat 31 | ====================== 32 | " >> "$TMP" 33 | 34 | echo "Including PR" >> "$TMP" 35 | cat "$1" >> "$TMP" 36 | echo "Base branch" >> "$TMP" 37 | cat "$2" >> "$TMP" 38 | 39 | COMMENT="$(cat $TMP | sed "s/$WARNING//g" | sed 's/%/%25/g' | sed -z 's/\n/%0A/g')" 40 | # No output for equality is intentional. 41 | if (( $(echo "$NEW_SIZE > $OLD_SIZE" | bc -l) )); then 42 | echo "::warning file=.github/workflows/cargo_bloat.yml,title=Binary size::$COMMENT" 43 | fi 44 | if (( $(echo "$NEW_SIZE < $OLD_SIZE" | bc -l) )); then 45 | echo "::notice file=.github/workflows/cargo_bloat.yml,title=Binary size::$COMMENT" 46 | fi 47 | -------------------------------------------------------------------------------- /.github/workflows/cargo_audit.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' 5 | 6 | jobs: 7 | audit: 8 | runs-on: ubuntu-latest 9 | if: github.repository == 'google/OpenSK' 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | submodules: "true" 14 | - uses: actions/setup-python@v5 15 | with: 16 | python-version: "3.10" 17 | - name: Set up OpenSK 18 | run: ./setup.sh 19 | - uses: actions-rs/audit-check@v1 20 | with: 21 | token: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/cargo_bloat.yml: -------------------------------------------------------------------------------- 1 | name: Binary size report 2 | on: pull_request 3 | 4 | jobs: 5 | cargo_bloat: 6 | runs-on: ubuntu-latest 7 | steps: 8 | # Setup 9 | - uses: actions/setup-python@v5 10 | with: 11 | python-version: "3.10" 12 | - uses: actions-rs/cargo@v1 13 | with: 14 | command: install 15 | args: cargo-bloat 16 | 17 | # First run: PR 18 | - uses: actions/checkout@v4 19 | with: 20 | submodules: true 21 | - name: Set up OpenSK 22 | run: ./setup.sh 23 | - name: Run bloat on the PR 24 | run: RUSTFLAGS="-C link-arg=-icf=all -C force-frame-pointers=no -C link-arg=-Tnrf52840_layout.ld" cargo bloat --release --target=thumbv7em-none-eabi --features=config_command,with_ctap1 --crates >> .github/workflows/bloat_output_new.txt 25 | 26 | # Second run: PR 27 | - uses: actions/checkout@v4 28 | with: 29 | submodules: true 30 | ref: ${{ github.base_ref }} 31 | path: OpenSK_base 32 | - name: Set up OpenSK 33 | working-directory: ./OpenSK_base 34 | run: ./setup.sh 35 | - name: Run bloat on base 36 | working-directory: ./OpenSK_base 37 | run: RUSTFLAGS="-C link-arg=-icf=all -C force-frame-pointers=no -C link-arg=-Tnrf52840_layout.ld" cargo bloat --release --target=thumbv7em-none-eabi --features=config_command,with_ctap1 --crates >> "$GITHUB_WORKSPACE/.github/workflows/bloat_output_old.txt" 38 | 39 | - name: Run output formatter to echo workflow command 40 | run: ./.github/workflows/bloat_formatter.sh bloat_output_new.txt bloat_output_old.txt bloat_comment.md 41 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | pull_request: 8 | branches: 9 | - develop 10 | schedule: 11 | - cron: 30 1 * * 2 # every Tuesday at 1:30 UTC 12 | 13 | concurrency: 14 | group: ci-${{ github.ref }} 15 | cancel-in-progress: ${{ github.event_name == 'pull_request' }} 16 | 17 | jobs: 18 | runtests: 19 | strategy: 20 | matrix: 21 | os: [ubuntu-latest, macos-latest] 22 | runs-on: ${{ matrix.os }} 23 | permissions: 24 | contents: read 25 | steps: 26 | - uses: actions/checkout@v4 27 | - uses: actions/setup-python@v5 28 | with: 29 | python-version: "3.10" 30 | - run: ./setup.sh 31 | - run: ./run_desktop_tests.sh 32 | -------------------------------------------------------------------------------- /.github/workflows/cifuzz.yml: -------------------------------------------------------------------------------- 1 | name: CIFuzz 2 | on: 3 | push: 4 | branches: 5 | - develop 6 | jobs: 7 | Fuzzing: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Build Fuzzers 11 | id: build 12 | uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master 13 | with: 14 | oss-fuzz-project-name: 'opensk' 15 | dry-run: false 16 | language: rust 17 | - name: Run Fuzzers 18 | uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master 19 | with: 20 | oss-fuzz-project-name: 'opensk' 21 | fuzz-seconds: 600 22 | dry-run: false 23 | - name: Upload Crash 24 | uses: actions/upload-artifact@v4 25 | if: failure() && steps.build.outcome == 'success' 26 | with: 27 | name: artifacts 28 | path: ./out/artifacts 29 | -------------------------------------------------------------------------------- /.github/workflows/coveralls.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: OpenSK code coverage report 3 | on: 4 | push: 5 | paths: 6 | - 'libraries/**/*.rs' 7 | pull_request: 8 | types: [opened, synchronize, reopened] 9 | 10 | jobs: 11 | coveralls: 12 | name: OpenSK code coverage 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | submodules: "true" 19 | - name: Install Rust toolchain 20 | run: rustup show 21 | - uses: actions/setup-python@v5 22 | with: 23 | python-version: "3.10" 24 | - name: Set up OpenSK 25 | run: ./setup.sh 26 | - name: Install llvm tools 27 | run: rustup +nightly component add llvm-tools-preview 28 | 29 | - name: Install grcov 30 | run: if [[ ! -e ~/.cargo/bin/grcov ]]; then cargo +stable install grcov; fi 31 | - uses: actions-rs/cargo@v1 32 | with: 33 | toolchain: nightly 34 | command: test 35 | args: --manifest-path libraries/opensk/Cargo.toml --features "std,with_ctap1,vendor_hid,ed25519" --no-fail-fast 36 | env: 37 | RUSTFLAGS: "-Cinstrument-coverage" 38 | LLVM_PROFILE_FILE: "opensk-%p-%m.profraw" 39 | - name: Run grcov 40 | run: RUSTUP_TOOLCHAIN=nightly grcov . --binary-path ./libraries/opensk/target/debug/ --source-dir libraries/opensk/ --output-type lcov --ignore-not-existing --output-path ./lcov.info --ignore "/*" --ignore "examples/*" --ignore "third_party/*" 41 | - uses: coverallsapp/github-action@1.1.3 42 | name: upload report to coveralls 43 | with: 44 | github-token: ${{ secrets.GITHUB_TOKEN }} 45 | path-to-lcov: "./lcov.info" 46 | base-path: "libraries/opensk" 47 | 48 | -------------------------------------------------------------------------------- /.github/workflows/mdlint.yml: -------------------------------------------------------------------------------- 1 | name: markdownlint 2 | on: 3 | push: 4 | paths: 5 | - '**/*.md' 6 | - '.markdownlint.json' 7 | - '!third_party/**' 8 | pull_request: 9 | types: [opened, synchronize, reopened] 10 | 11 | jobs: 12 | mdlint: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: markdownlint-cli 17 | uses: nosborn/github-action-markdown-cli@v3 18 | with: 19 | files: '**/*.md' 20 | config_file: '.markdownlint.json' 21 | ignore_files: 'third_party/*' 22 | -------------------------------------------------------------------------------- /.github/workflows/reproducible.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Check that binaries are reproducible 3 | on: 4 | push: 5 | pull_request: 6 | types: [opened, synchronize, reopened] 7 | 8 | jobs: 9 | check_hashes: 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest, macos-latest] 13 | fail-fast: false 14 | runs-on: ${{ matrix.os }} 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | submodules: "true" 19 | - name: Install Rust toolchain 20 | run: rustup show 21 | - uses: actions/setup-python@v5 22 | with: 23 | python-version: "3.10" 24 | - name: Set up OpenSK 25 | run: ./setup.sh 26 | 27 | - name: Use sample cryptographic material 28 | run: rm -R crypto_data/ && cp -r reproducible/sample_crypto_data crypto_data 29 | - name: Computing cryptographic hashes 30 | run: ./maintainers/reproduce_hashes.sh 31 | 32 | - name: Upload reproduced binaries 33 | uses: actions/upload-artifact@v4 34 | with: 35 | name: reproduced-${{ matrix.os }} 36 | path: reproducible/reproduced.tar 37 | 38 | - name: Comparing binary sizes 39 | if: always() 40 | run: git diff --no-index reproducible/reference_elf2tab_${{ matrix.os }}.txt reproducible/elf2tab.txt || true 41 | - name: Comparing cryptographic hashes 42 | if: always() 43 | run: git diff --no-index reproducible/reference_binaries_${{ matrix.os }}.sha256sum reproducible/binaries.sha256sum || true 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | /build/ 3 | /py_virtual_env/ 4 | 5 | # Local installation of elf2tab. 6 | /elf2tab/ 7 | 8 | # Prevent people from commiting sensitive files. 9 | /crypto_data/ 10 | 11 | # Temporary files. 12 | /reproducible/binaries.sha256sum 13 | /reproducible/elf2tab.txt 14 | /reproducible/reproduced.tar 15 | __pycache__ 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/libtock-rs"] 2 | path = third_party/libtock-rs 3 | url = https://github.com/tock/libtock-rs 4 | ignore = dirty 5 | [submodule "third_party/tock"] 6 | path = third_party/tock 7 | url = https://github.com/tock/tock 8 | ignore = dirty 9 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "MD003": { 4 | "style": "atx" 5 | }, 6 | "MD007": { 7 | "indent": 4 8 | }, 9 | "MD009": { 10 | "br_spaces": 0, 11 | "strict": true 12 | }, 13 | "MD013": { 14 | "line_length": 80, 15 | "code_blocks": false 16 | }, 17 | "MD033": { 18 | "allowed_elements": [ 19 | "img" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | repository-code: "https://github.com/google/OpenSK" 4 | license: "Apache-2.0" 5 | preferred-citation: 6 | type: article 7 | authors: 8 | - family-names: "Ghinea" 9 | given-names: "Diana" 10 | - family-names: "Kaczmarczyck" 11 | given-names: "Fabian" 12 | - family-names: "Pullman" 13 | given-names: "Jennifer" 14 | - family-names: "Cretin" 15 | given-names: "Julien" 16 | - family-names: "Kölbl" 17 | given-names: "Stefan" 18 | - family-names: "Invernizzi" 19 | given-names: "Luca" 20 | - family-names: "Bursztein" 21 | given-names: "Elie" 22 | - family-names: "Picod" 23 | given-names: "Jean-Michel" 24 | title: "Hybrid Post-Quantum Signatures in Hardware Security Keys" 25 | journal: "4th ACNS Workshop on Secure Cryptographic Implementation" 26 | year: 2023 27 | month: 6 28 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ctap2" 3 | version = "1.0.0" 4 | authors = [ 5 | "Fabian Kaczmarczyck ", 6 | "Guillaume Endignoux ", 7 | "Jean-Michel Picod ", 8 | "Julien Cretin ", 9 | ] 10 | license = "Apache-2.0" 11 | edition = "2018" 12 | 13 | [target.'cfg(any(target_arch = "arm", target_arch = "riscv32"))'.dependencies.libtock_runtime] 14 | path = "third_party/libtock-rs/runtime" 15 | default-features = false 16 | features = ["no_auto_layout", "no_debug_memop"] 17 | 18 | [dependencies] 19 | libtock_buttons = { path = "third_party/libtock-rs/apis/buttons" } 20 | libtock_platform = { path = "third_party/libtock-rs/platform" } 21 | libtock_drivers = { path = "third_party/libtock-drivers" } 22 | libtock_alarm = { path = "third_party/libtock-rs/apis/alarm" } 23 | libtock_console = { path = "third_party/libtock-rs/apis/console" } 24 | libtock_leds = { path = "third_party/libtock-rs/apis/leds" } 25 | lang_items = { path = "third_party/lang-items" } 26 | opensk = { path = "libraries/opensk", default-features = false } 27 | sk-cbor = { path = "libraries/cbor" } 28 | persistent_store = { path = "libraries/persistent_store" } 29 | libtock_unittest = { path = "third_party/libtock-rs/unittest", optional = true } 30 | byteorder = { version = "1", default-features = false } 31 | arrayref = "0.3.6" 32 | rand_core = "0.6.4" 33 | ed25519-compact = { version = "1", default-features = false, optional = true } 34 | 35 | [features] 36 | config_command = ["opensk/config_command"] 37 | debug_allocations = ["lang_items/debug_allocations"] 38 | debug_ctap = ["libtock_drivers/debug_ctap", "opensk/debug_ctap"] 39 | panic_console = ["lang_items/panic_console"] 40 | std = ["lang_items/std", "persistent_store/std", "opensk/std", "libtock_unittest"] 41 | verbose = ["debug_ctap", "libtock_drivers/verbose_usb"] 42 | with_ctap1 = ["opensk/with_ctap1"] 43 | with_nfc = ["libtock_drivers/with_nfc"] 44 | vendor_hid = ["opensk/vendor_hid"] 45 | ed25519 = ["ed25519-compact", "opensk/ed25519"] 46 | 47 | [dev-dependencies] 48 | enum-iterator = "0.6.0" 49 | 50 | [build-dependencies] 51 | sk-cbor = { path = "libraries/cbor" } 52 | uuid = { version = "0.8", features = ["v4"] } 53 | openssl = "0.10.55" 54 | 55 | [profile.dev] 56 | panic = "abort" 57 | lto = true # Link Time Optimization usually reduces size of binaries and static libraries 58 | 59 | [profile.release] 60 | panic = "abort" 61 | lto = true # Link Time Optimization usually reduces size of binaries and static libraries 62 | opt-level = "z" 63 | codegen-units = 1 64 | -------------------------------------------------------------------------------- /OpenSK.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "name": "OpenSK", 5 | "path": "." 6 | }, 7 | { 8 | "name": "tock", 9 | "path": "third_party/tock" 10 | }, 11 | { 12 | "name": "libtock-rs", 13 | "path": "third_party/libtock-rs" 14 | }, 15 | { 16 | "name": "libtock-drivers", 17 | "path": "third_party/libtock-drivers" 18 | } 19 | ], 20 | "settings": { 21 | "clang-format.fallbackStyle": "google", 22 | "editor.detectIndentation": true, 23 | "editor.formatOnPaste": false, 24 | "editor.formatOnSave": true, 25 | "editor.formatOnType": true, 26 | "editor.insertSpaces": true, 27 | "editor.tabSize": 4, 28 | "files.insertFinalNewline": true, 29 | "files.trimTrailingWhitespace": true, 30 | // Ensure we use the toolchain we set in rust-toolchain file 31 | "rust-client.channel": "default", 32 | // The toolchain is updated from time to time so let's make sure that RLS is updated too 33 | "rust-client.updateOnStartup": true, 34 | "rust.clippy_preference": "on", 35 | "rust.target": "thumbv7em-none-eabi", 36 | "rust.all_targets": false, 37 | // Try to make VSCode formating as close as possible to the Google style. 38 | "python.formatting.provider": "yapf", 39 | "python.formatting.yapfArgs": [ 40 | "--style=yapf" 41 | ], 42 | "python.linting.enabled": true, 43 | "python.linting.lintOnSave": true, 44 | "python.linting.pylintEnabled": true, 45 | "python.linting.pylintPath": "pylint", 46 | "[python]": { 47 | "editor.tabSize": 2 48 | }, 49 | }, 50 | "extensions": { 51 | "recommendations": [ 52 | "davidanson.vscode-markdownlint", 53 | "rust-lang.rust", 54 | "ms-python.python" 55 | ] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | To report a security issue, please use http://g.co/vulnz. We use 2 | http://g.co/vulnz for our intake, and do coordination and disclosure here on 3 | GitHub (including using GitHub Security Advisory). The Google Security Team will 4 | respond within 5 working days of your report on g.co/vulnz. 5 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_dongle_dfu/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nrf52840_dongle_dfu" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | build = "build.rs" 6 | edition = "2018" 7 | 8 | [[bin]] 9 | path = "../nrf52840_dongle_opensk/src/main.rs" 10 | name = "nrf52840_dongle_dfu" 11 | 12 | [dependencies] 13 | components = { path = "../../components" } 14 | cortexm4 = { path = "../../../arch/cortex-m4" } 15 | capsules = { path = "../../../capsules" } 16 | kernel = { path = "../../../kernel" } 17 | nrf52840 = { path = "../../../chips/nrf52840" } 18 | nrf52_components = { path = "../nrf52_components" } 19 | 20 | [features] 21 | vendor_hid = ["capsules/vendor_hid"] 22 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_dongle_dfu/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for building the tock kernel for the nRF development kit 2 | 3 | TOCK_ARCH=cortex-m4 4 | TARGET=thumbv7em-none-eabi 5 | PLATFORM=nrf52840_dongle_dfu 6 | 7 | include ../../Makefile.common 8 | 9 | TOCKLOADER=tockloader 10 | 11 | # Where in the nrf52 flash to load the kernel with `tockloader` 12 | KERNEL_ADDRESS=0x01000 13 | 14 | # Upload programs over uart with tockloader 15 | ifdef PORT 16 | TOCKLOADER_GENERAL_FLAGS += --port $(PORT) 17 | endif 18 | 19 | TOCKLOADER_JTAG_FLAGS = --jlink --arch $(TOCK_ARCH) --board $(PLATFORM) --page-size 4096 --jlink-device nrf52840_xxaa 20 | 21 | # Upload the kernel over JTAG 22 | .PHONY: flash 23 | flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin 24 | $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) $(TOCKLOADER_JTAG_FLAGS) $< 25 | 26 | # Upload the kernel over serial/bootloader 27 | .PHONY: program 28 | program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).hex 29 | $(error Cannot program nRF52 Dongle over USB. Use \`make flash\` and JTAG) 30 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_dongle_dfu/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rerun-if-changed=layout.ld"); 3 | println!("cargo:rerun-if-changed=../../kernel_layout.ld"); 4 | } 5 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_dongle_dfu/layout.ld: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | rom (rx) : ORIGIN = 0x00001000, LENGTH = 188K 4 | prog (rx) : ORIGIN = 0x00030000, LENGTH = 832K 5 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 256K 6 | } 7 | 8 | MPU_MIN_ALIGN = 8K; 9 | 10 | INCLUDE ../../kernel_layout.ld 11 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_dongle_opensk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nrf52840_dongle_opensk" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | build = "build.rs" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | components = { path = "../../components" } 10 | cortexm4 = { path = "../../../arch/cortex-m4" } 11 | capsules = { path = "../../../capsules" } 12 | kernel = { path = "../../../kernel" } 13 | nrf52840 = { path = "../../../chips/nrf52840" } 14 | nrf52_components = { path = "../nrf52_components" } 15 | 16 | [features] 17 | vendor_hid = ["capsules/vendor_hid"] 18 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_dongle_opensk/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for building the tock kernel for the nRF development kit 2 | 3 | TARGET=thumbv7em-none-eabi 4 | PLATFORM=nrf52840_dongle_opensk 5 | 6 | include ../../Makefile.common 7 | 8 | TOCKLOADER=tockloader 9 | 10 | # Where in the nrf52 flash to load the kernel with `tockloader` 11 | KERNEL_ADDRESS=0x00000 12 | 13 | # Upload programs over uart with tockloader 14 | ifdef PORT 15 | TOCKLOADER_GENERAL_FLAGS += --port $(PORT) 16 | endif 17 | 18 | TOCKLOADER_JTAG_FLAGS = --jlink --board nrf52dk 19 | 20 | # Upload the kernel over JTAG 21 | .PHONY: flash 22 | flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin 23 | $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) $(TOCKLOADER_JTAG_FLAGS) $< 24 | 25 | # Upload the kernel over serial/bootloader 26 | .PHONY: program 27 | program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).hex 28 | $(error Cannot program nRF52840-Dongle over USB. Use \`make flash\` and JTAG) 29 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_dongle_opensk/README.md: -------------------------------------------------------------------------------- 1 | Platform-Specific Instructions: nRF52840-Dongle 2 | =================================== 3 | 4 | This is an adapted nrf52840\_dongle made to work with OpenSK. 5 | 6 | The [nRF52840 Dongle](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle) 7 | is a platform based around the nRF52840, an SoC with an ARM Cortex-M4 and a BLE radio. 8 | The kit is uses a USB key form factor and includes 1 button, 1 red LED and 1 RGB LED. 9 | 10 | ## Getting Started 11 | 12 | To program the nRF52840 Dongle with Tock, you will need a JLink JTAG device and the 13 | appropriate cables. An example setup is: 14 | 15 | - [JLink JTAG Device](https://www.digikey.com/product-detail/en/segger-microcontroller-systems/8.08.90-J-LINK-EDU/899-1008-ND/2263130) 16 | - [ARM to TagConnect Adapter](https://www.digikey.com/product-detail/en/tag-connect-llc/TC2050-ARM2010/TC2050-ARM2010-ND/3528170) 17 | - [10pin TagConnect Cable](https://www.digikey.com/product-detail/en/tag-connect-llc/TC2050-IDC-NL/TC2050-IDC-NL-ND/2605367) 18 | 19 | Then, follow the [Tock Getting Started guide](../../../doc/Getting_Started.md) 20 | 21 | JTAG is the preferred method to program. The development kit has the JTAG pins exposed either 22 | through the half-moons pads or, below the PCB, on a Tag-Connect TC2050 connector footprint. 23 | You need to [install JTAG software](../../../doc/Getting_Started.md#optional-requirements). 24 | 25 | ## Programming the kernel 26 | Once you have all software installed, you should be able to simply run 27 | make flash in this directory to install a fresh kernel. 28 | 29 | ## Programming user-level applications 30 | You can program an application via JTAG using `tockloader`: 31 | 32 | ```shell 33 | $ cd libtock-c/examples/ 34 | $ make 35 | $ tockloader install --jlink --board nrf52dk 36 | ``` 37 | 38 | ## Debugging 39 | 40 | See the [nrf52dk README](../nrf52dk/README.md) for information about debugging 41 | the nRF52840 Dongle. 42 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_dongle_opensk/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rerun-if-changed=layout.ld"); 3 | println!("cargo:rerun-if-changed=../../kernel_layout.ld"); 4 | } 5 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_dongle_opensk/jtag/gdbinit_pca10040.jlink: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # 4 | # J-LINK GDB SERVER initialization 5 | # 6 | # This connects to a GDB Server listening 7 | # for commands on localhost at tcp port 2331 8 | target remote localhost:2331 9 | monitor speed 30 10 | file ../../../../target/thumbv7em-none-eabi/release/nrf52840_dongle 11 | monitor reset 12 | # 13 | # CPU core initialization (to be done by user) 14 | # 15 | # Set the processor mode 16 | # monitor reg cpsr = 0xd3 17 | # Set auto JTAG speed 18 | monitor speed auto 19 | # Setup GDB FOR FASTER DOWNLOADS 20 | set remote memory-write-packet-size 1024 21 | set remote memory-write-packet-size fixed 22 | # tui enable 23 | # layout split 24 | # layout service_pending_interrupts 25 | b reset_handler 26 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_dongle_opensk/jtag/jdbserver_pca10040.sh: -------------------------------------------------------------------------------- 1 | JLinkGDBServer -device nRF52840_xxAA -speed 1200 -if swd -AutoConnect 1 -port 2331 2 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_dongle_opensk/layout.ld: -------------------------------------------------------------------------------- 1 | INCLUDE ../nrf52840_chip_layout.ld 2 | INCLUDE ../../kernel_layout.ld 3 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_dongle_opensk/src/io.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Write; 2 | use core::panic::PanicInfo; 3 | use cortexm4; 4 | use kernel::debug; 5 | use kernel::debug::IoWrite; 6 | use kernel::hil::led; 7 | use kernel::hil::uart::{self, Configure}; 8 | use nrf52840::gpio::Pin; 9 | 10 | use crate::{CHIP, PROCESSES, PROCESS_PRINTER}; 11 | 12 | struct Writer { 13 | initialized: bool, 14 | } 15 | 16 | static mut WRITER: Writer = Writer { initialized: false }; 17 | 18 | impl Write for Writer { 19 | fn write_str(&mut self, s: &str) -> ::core::fmt::Result { 20 | self.write(s.as_bytes()); 21 | Ok(()) 22 | } 23 | } 24 | 25 | impl IoWrite for Writer { 26 | fn write(&mut self, buf: &[u8]) { 27 | // Here, we create a second instance of the Uarte struct. 28 | // This is okay because we only call this during a panic, and 29 | // we will never actually process the interrupts 30 | let uart = nrf52840::uart::Uarte::new(); 31 | if !self.initialized { 32 | self.initialized = true; 33 | let _ = uart.configure(uart::Parameters { 34 | baud_rate: 115200, 35 | stop_bits: uart::StopBits::One, 36 | parity: uart::Parity::None, 37 | hw_flow_control: false, 38 | width: uart::Width::Eight, 39 | }); 40 | } 41 | for &c in buf { 42 | unsafe { 43 | uart.send_byte(c); 44 | } 45 | while !uart.tx_ready() {} 46 | } 47 | } 48 | } 49 | 50 | #[cfg(not(test))] 51 | #[no_mangle] 52 | #[panic_handler] 53 | /// Panic handler 54 | pub unsafe extern "C" fn panic_fmt(pi: &PanicInfo) -> ! { 55 | // The nRF52840 Dongle LEDs (see back of board) 56 | let led_kernel_pin = &nrf52840::gpio::GPIOPin::new(Pin::P0_06); 57 | let led = &mut led::LedLow::new(led_kernel_pin); 58 | let writer = &mut WRITER; 59 | debug::panic( 60 | &mut [led], 61 | writer, 62 | pi, 63 | &cortexm4::support::nop, 64 | &PROCESSES, 65 | &CHIP, 66 | &PROCESS_PRINTER, 67 | ) 68 | } 69 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_mdk_dfu/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nrf52840_mdk_dfu" 3 | version = "0.1.0" 4 | authors = ["Yihui Xiong "] 5 | build = "build.rs" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | components = { path = "../../components" } 10 | cortexm4 = { path = "../../../arch/cortex-m4" } 11 | capsules = { path = "../../../capsules" } 12 | kernel = { path = "../../../kernel" } 13 | nrf52840 = { path = "../../../chips/nrf52840" } 14 | nrf52_components = { path = "../nrf52_components" } 15 | 16 | [features] 17 | vendor_hid = ["capsules/vendor_hid"] 18 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_mdk_dfu/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for building the tock kernel for the nRF development kit 2 | 3 | TOCK_ARCH=cortex-m4 4 | TARGET=thumbv7em-none-eabi 5 | PLATFORM=nrf52840_mdk_dfu 6 | 7 | include ../../Makefile.common 8 | 9 | TOCKLOADER=tockloader 10 | 11 | # Where in the nrf52 flash to load the kernel with `tockloader` 12 | KERNEL_ADDRESS=0x01000 13 | 14 | # Upload programs over uart with tockloader 15 | ifdef PORT 16 | TOCKLOADER_GENERAL_FLAGS += --port $(PORT) 17 | endif 18 | 19 | TOCKLOADER_JTAG_FLAGS = --jlink --arch $(TOCK_ARCH) --board $(PLATFORM) --page-size 4096 --jlink-device nrf52840_xxaa 20 | 21 | # Upload the kernel over JTAG 22 | .PHONY: flash 23 | flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin 24 | $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) $(TOCKLOADER_JTAG_FLAGS) $< 25 | 26 | # Upload the kernel over serial/bootloader 27 | .PHONY: program 28 | program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).hex 29 | $(error Cannot program nRF52840-MDK over USB. Use \`make flash\` and JTAG) 30 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_mdk_dfu/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rerun-if-changed=layout.ld"); 3 | println!("cargo:rerun-if-changed=../../kernel_layout.ld"); 4 | } 5 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_mdk_dfu/layout.ld: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | rom (rx) : ORIGIN = 0x00001000, LENGTH = 188K 4 | prog (rx) : ORIGIN = 0x00030000, LENGTH = 832K 5 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 256K 6 | } 7 | 8 | MPU_MIN_ALIGN = 8K; 9 | 10 | INCLUDE ../../kernel_layout.ld 11 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840_mdk_dfu/src/io.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Write; 2 | use core::panic::PanicInfo; 3 | use cortexm4; 4 | use kernel::debug; 5 | use kernel::debug::IoWrite; 6 | use kernel::hil::led; 7 | use kernel::hil::uart::{self, Configure}; 8 | use nrf52840::gpio::Pin; 9 | 10 | use crate::{CHIP, PROCESSES, PROCESS_PRINTER}; 11 | 12 | struct Writer { 13 | initialized: bool, 14 | } 15 | 16 | static mut WRITER: Writer = Writer { initialized: false }; 17 | 18 | impl Write for Writer { 19 | fn write_str(&mut self, s: &str) -> ::core::fmt::Result { 20 | self.write(s.as_bytes()); 21 | Ok(()) 22 | } 23 | } 24 | 25 | impl IoWrite for Writer { 26 | fn write(&mut self, buf: &[u8]) { 27 | // Here, we create a second instance of the Uarte struct. 28 | // This is okay because we only call this during a panic, and 29 | // we will never actually process the interrupts 30 | let uart = nrf52840::uart::Uarte::new(); 31 | if !self.initialized { 32 | self.initialized = true; 33 | let _ = uart.configure(uart::Parameters { 34 | baud_rate: 115200, 35 | stop_bits: uart::StopBits::One, 36 | parity: uart::Parity::None, 37 | hw_flow_control: false, 38 | width: uart::Width::Eight, 39 | }); 40 | } 41 | for &c in buf { 42 | unsafe { 43 | uart.send_byte(c); 44 | } 45 | while !uart.tx_ready() {} 46 | } 47 | } 48 | } 49 | 50 | #[cfg(not(test))] 51 | #[no_mangle] 52 | #[panic_handler] 53 | /// Panic handler 54 | pub unsafe extern "C" fn panic_fmt(pi: &PanicInfo) -> ! { 55 | // The nRF52840 Dongle LEDs (see back of board) 56 | let led_kernel_pin = &nrf52840::gpio::GPIOPin::new(Pin::P0_23); 57 | let led = &mut led::LedLow::new(led_kernel_pin); 58 | let writer = &mut WRITER; 59 | debug::panic( 60 | &mut [led], 61 | writer, 62 | pi, 63 | &cortexm4::support::nop, 64 | &PROCESSES, 65 | &CHIP, 66 | &PROCESS_PRINTER, 67 | ) 68 | } 69 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nrf52840dk_opensk" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | build = "build.rs" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | components = { path = "../../components" } 10 | cortexm4 = { path = "../../../arch/cortex-m4" } 11 | capsules = { path = "../../../capsules" } 12 | kernel = { path = "../../../kernel" } 13 | nrf52840 = { path = "../../../chips/nrf52840" } 14 | nrf52_components = { path = "../nrf52_components" } 15 | 16 | [features] 17 | vendor_hid = ["capsules/vendor_hid"] 18 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for building the tock kernel for the nRF development kit 2 | 3 | TARGET=thumbv7em-none-eabi 4 | PLATFORM=nrf52840dk_opensk 5 | 6 | include ../../Makefile.common 7 | 8 | TOCKLOADER=tockloader 9 | 10 | # Where in the SAM4L flash to load the kernel with `tockloader` 11 | KERNEL_ADDRESS=0x00000 12 | 13 | # Upload programs over uart with tockloader 14 | ifdef PORT 15 | TOCKLOADER_GENERAL_FLAGS += --port $(PORT) 16 | endif 17 | 18 | # Upload the kernel over JTAG 19 | .PHONY: flash 20 | flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin 21 | $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) --board nrf52dk --jlink $< 22 | 23 | # Upload the kernel over JTAG using OpenOCD 24 | .PHONY: flash-openocd 25 | flash-openocd: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin 26 | $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) --board nrf52dk --openocd $< 27 | 28 | # Upload the kernel over serial/bootloader 29 | .PHONY: program 30 | program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).hex 31 | $(error Cannot program nRF52840DK over USB. Use \`make flash\` and JTAG) 32 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk/README.md: -------------------------------------------------------------------------------- 1 | Platform-Specific Instructions: nRF52840-DK 2 | =================================== 3 | 4 | This is an adapted nrf52840dk made to work with OpenSK. 5 | 6 | The [nRF52840 Development 7 | Kit](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) is a platform 8 | based around the nRF52840, an SoC with an ARM Cortex-M4 and a BLE 9 | radio. The kit is Arduino shield compatible and includes several 10 | buttons. 11 | 12 | ## Getting Started 13 | 14 | First, follow the [Tock Getting Started guide](../../../doc/Getting_Started.md) 15 | 16 | JTAG is the preferred method to program. The development kit has an 17 | integrated JTAG debugger, you simply need to [install JTAG 18 | software](../../../doc/Getting_Started.md#loading-the-kernel-onto-a-board). 19 | 20 | ## Programming the kernel 21 | Once you have all software installed, you should be able to simply run 22 | make flash in this directory to install a fresh kernel. 23 | 24 | ## Programming user-level applications 25 | You can program an application over USB using the integrated JTAG and `tockloader`: 26 | 27 | ```bash 28 | $ cd libtock-c/examples/ 29 | $ make 30 | $ tockloader install --jlink --board nrf52dk 31 | ``` 32 | 33 | The same options (`--jlink --board nrf52dk`) must be passed for other tockloader commands 34 | such as `erase-apps` or `list`. 35 | 36 | Viewing console output on the nrf52840dk is slightly different from other boards. You must use 37 | ```bash 38 | $ tockloader listen 39 | ``` 40 | **followed by a press of the reset button** in order to view console output starting from the boot 41 | sequence. Notably, you should not 42 | pass the `--jlink` option to `tockloader listen`. 43 | 44 | ## Console output 45 | 46 | This board supports two methods for writing messages to a console interface 47 | (console driver for applications as well as debug statements in the kernel). 48 | 49 | By default, messages are written to a UART interface over the GPIO pins `P0.05` 50 | to `P0.08` (see the [main.rs](src/main.rs) file). 51 | 52 | If you don't have any UART cables or want to use a different interface, there is 53 | also a console over the Segger RTT protocol. This only requires a micro-USB 54 | cable on the USB debugging port (the same used to flash Tock on the board), and 55 | is enabled by setting the `USB_DEBUGGING` constant to `true` in the 56 | [main.rs](src/main.rs) file. 57 | This disables the UART interface. 58 | 59 | For instructions about how to receive RTT messages on the host, see the 60 | [corresponding capsule](../../../capsules/src/segger_rtt.rs). 61 | 62 | ## Debugging 63 | 64 | See the [nrf52dk README](../nrf52dk/README.md) for information about debugging 65 | the nRF52840dk. 66 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::path::Path; 4 | 5 | fn main() { 6 | println!("cargo:rerun-if-changed=layout.ld"); 7 | println!("cargo:rerun-if-changed=../../kernel_layout.ld"); 8 | 9 | let out_dir = env::var_os("OUT_DIR").unwrap(); 10 | let dest_path = Path::new(&out_dir).join("locations.rs"); 11 | fs::write( 12 | &dest_path, 13 | " 14 | static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 2] = [ 15 | // We implement NUM_PAGES = 20 as 16 + 4 to satisfy the MPU. 16 | kernel::StorageLocation { 17 | address: 0xC0000, 18 | size: 0x10000, // 16 pages 19 | storage_type: kernel::StorageType::Store, 20 | }, 21 | kernel::StorageLocation { 22 | address: 0xD0000, 23 | size: 0x4000, // 4 pages 24 | storage_type: kernel::StorageType::Store, 25 | }, 26 | ]; 27 | " 28 | ).unwrap(); 29 | } 30 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk/jtag/gdbinit_pca10040.jlink: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # 4 | # J-LINK GDB SERVER initialization 5 | # 6 | # This connects to a GDB Server listening 7 | # for commands on localhost at tcp port 2331 8 | target remote localhost:2331 9 | monitor speed 30 10 | file ../../../../target/thumbv7em-none-eabi/release/nrf52dk 11 | monitor reset 12 | # 13 | # CPU core initialization (to be done by user) 14 | # 15 | # Set the processor mode 16 | # monitor reg cpsr = 0xd3 17 | # Set auto JTAG speed 18 | monitor speed auto 19 | # Setup GDB FOR FASTER DOWNLOADS 20 | set remote memory-write-packet-size 1024 21 | set remote memory-write-packet-size fixed 22 | # tui enable 23 | # layout split 24 | # layout service_pending_interrupts 25 | b reset_handler 26 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk/jtag/jdbserver_pca10040.sh: -------------------------------------------------------------------------------- 1 | JLinkGDBServer -device nrf52 -speed 1200 -if swd -AutoConnect 1 -port 2331 2 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk/layout.ld: -------------------------------------------------------------------------------- 1 | INCLUDE ../nrf52840_chip_layout.ld 2 | INCLUDE ../../kernel_layout.ld 3 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk/src/io.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Write; 2 | use core::panic::PanicInfo; 3 | use cortexm4; 4 | use kernel::debug; 5 | use kernel::debug::IoWrite; 6 | use kernel::hil::led; 7 | use kernel::hil::uart; 8 | use kernel::hil::uart::Configure; 9 | use nrf52840::gpio::Pin; 10 | 11 | use crate::CHIP; 12 | use crate::PROCESSES; 13 | use crate::PROCESS_PRINTER; 14 | 15 | enum Writer { 16 | WriterUart(/* initialized */ bool), 17 | WriterRtt(&'static capsules::segger_rtt::SeggerRttMemory<'static>), 18 | } 19 | 20 | static mut WRITER: Writer = Writer::WriterUart(false); 21 | 22 | fn wait() { 23 | for _ in 0..100 { 24 | cortexm4::support::nop(); 25 | } 26 | } 27 | 28 | /// Set the RTT memory buffer used to output panic messages. 29 | pub unsafe fn set_rtt_memory( 30 | rtt_memory: &'static mut capsules::segger_rtt::SeggerRttMemory<'static>, 31 | ) { 32 | WRITER = Writer::WriterRtt(rtt_memory); 33 | } 34 | 35 | impl Write for Writer { 36 | fn write_str(&mut self, s: &str) -> ::core::fmt::Result { 37 | self.write(s.as_bytes()); 38 | Ok(()) 39 | } 40 | } 41 | 42 | impl IoWrite for Writer { 43 | fn write(&mut self, buf: &[u8]) { 44 | match self { 45 | Writer::WriterUart(ref mut initialized) => { 46 | // Here, we create a second instance of the Uarte struct. 47 | // This is okay because we only call this during a panic, and 48 | // we will never actually process the interrupts 49 | let uart = nrf52840::uart::Uarte::new(); 50 | if !*initialized { 51 | *initialized = true; 52 | let _ = uart.configure(uart::Parameters { 53 | baud_rate: 115200, 54 | stop_bits: uart::StopBits::One, 55 | parity: uart::Parity::None, 56 | hw_flow_control: false, 57 | width: uart::Width::Eight, 58 | }); 59 | } 60 | for &c in buf { 61 | unsafe { 62 | uart.send_byte(c); 63 | } 64 | while !uart.tx_ready() {} 65 | } 66 | } 67 | Writer::WriterRtt(rtt_memory) => { 68 | let up_buffer = unsafe { &*rtt_memory.get_up_buffer_ptr() }; 69 | let buffer_len = up_buffer.length.get(); 70 | let buffer = unsafe { 71 | core::slice::from_raw_parts_mut( 72 | up_buffer.buffer.get() as *mut u8, 73 | buffer_len as usize, 74 | ) 75 | }; 76 | 77 | let mut write_position = up_buffer.write_position.get(); 78 | 79 | for &c in buf { 80 | buffer[write_position as usize] = c; 81 | write_position = (write_position + 1) % buffer_len; 82 | up_buffer.write_position.set(write_position); 83 | wait(); 84 | } 85 | } 86 | }; 87 | } 88 | } 89 | 90 | #[cfg(not(test))] 91 | #[no_mangle] 92 | #[panic_handler] 93 | /// Panic handler 94 | pub unsafe extern "C" fn panic_fmt(pi: &PanicInfo) -> ! { 95 | // The nRF52840DK LEDs (see back of board) 96 | let led_kernel_pin = &nrf52840::gpio::GPIOPin::new(Pin::P0_13); 97 | let led = &mut led::LedLow::new(led_kernel_pin); 98 | let writer = &mut WRITER; 99 | debug::panic( 100 | &mut [led], 101 | writer, 102 | pi, 103 | &cortexm4::support::nop, 104 | &PROCESSES, 105 | &CHIP, 106 | &PROCESS_PRINTER, 107 | ) 108 | } 109 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk_a/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nrf52840dk_opensk_a" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | build = "build.rs" 6 | edition = "2018" 7 | 8 | [[bin]] 9 | path = "../nrf52840dk_opensk/src/main.rs" 10 | name = "nrf52840dk_opensk_a" 11 | 12 | [dependencies] 13 | components = { path = "../../components" } 14 | cortexm4 = { path = "../../../arch/cortex-m4" } 15 | capsules = { path = "../../../capsules" } 16 | kernel = { path = "../../../kernel" } 17 | nrf52840 = { path = "../../../chips/nrf52840" } 18 | nrf52_components = { path = "../nrf52_components" } 19 | 20 | [features] 21 | vendor_hid = ["capsules/vendor_hid"] 22 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk_a/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for building the tock kernel for the nRF development kit 2 | 3 | TARGET=thumbv7em-none-eabi 4 | PLATFORM=nrf52840dk_opensk_a 5 | 6 | include ../../Makefile.common 7 | 8 | TOCKLOADER=tockloader 9 | 10 | # Where in the SAM4L flash to load the kernel with `tockloader` 11 | KERNEL_ADDRESS=0x20000 12 | 13 | # Upload programs over uart with tockloader 14 | ifdef PORT 15 | TOCKLOADER_GENERAL_FLAGS += --port $(PORT) 16 | endif 17 | 18 | # Upload the kernel over JTAG 19 | .PHONY: flash 20 | flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin 21 | $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) --board nrf52dk --jlink $< 22 | 23 | # Upload the kernel over JTAG using OpenOCD 24 | .PHONY: flash-openocd 25 | flash-openocd: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin 26 | $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) --board nrf52dk --openocd $< 27 | 28 | # Upload the kernel over serial/bootloader 29 | .PHONY: program 30 | program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).hex 31 | $(error Cannot program nRF52840DK over USB. Use \`make flash\` and JTAG) 32 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk_a/README.md: -------------------------------------------------------------------------------- 1 | Platform-Specific Instructions: nRF52840-DK, partition A 2 | =================================== 3 | 4 | This is an upgrade partition for the adapted nrf52840dk in `../nrf52840dk_opensk`. 5 | 6 | Compared to our regular board definition for the nrf52840dk, changes are: 7 | - a `layout.ld` with 128 kB for kernel and app 8 | - the matching kernel address in the `Makefile` 9 | - different `StorageLocation`s in `build.rs` 10 | 11 | For everything else, please check the README in `../nrf52840dk_opensk`. 12 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk_a/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::path::Path; 4 | 5 | fn main() { 6 | println!("cargo:rerun-if-changed=layout.ld"); 7 | println!("cargo:rerun-if-changed=../../kernel_layout.ld"); 8 | 9 | let out_dir = env::var_os("OUT_DIR").unwrap(); 10 | let dest_path = Path::new(&out_dir).join("locations.rs"); 11 | fs::write( 12 | &dest_path, 13 | " 14 | static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 5] = [ 15 | // We implement NUM_PAGES = 20 as 16 + 4 to satisfy the MPU. 16 | kernel::StorageLocation { 17 | address: 0xC0000, 18 | size: 0x10000, // 16 pages 19 | storage_type: kernel::StorageType::Store, 20 | }, 21 | kernel::StorageLocation { 22 | address: 0xD0000, 23 | size: 0x4000, // 4 pages 24 | storage_type: kernel::StorageType::Store, 25 | }, 26 | // Partitions can also be split to maximize MPU happiness. 27 | kernel::StorageLocation { 28 | address: 0x4000, 29 | size: 0x2000, 30 | storage_type: kernel::StorageType::Partition, 31 | }, 32 | kernel::StorageLocation { 33 | address: 0x60000, 34 | size: 0x20000, 35 | storage_type: kernel::StorageType::Partition, 36 | }, 37 | kernel::StorageLocation { 38 | address: 0x80000, 39 | size: 0x20000, 40 | storage_type: kernel::StorageType::Partition, 41 | }, 42 | ]; 43 | " 44 | ).unwrap(); 45 | } 46 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk_a/layout.ld: -------------------------------------------------------------------------------- 1 | /* Memory Space Definitions, 1M flash, 256K ram */ 2 | MEMORY 3 | { 4 | rom (rx) : ORIGIN = 0x00020000, LENGTH = 128K 5 | prog (rx) : ORIGIN = 0x00040000, LENGTH = 128K 6 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 256K 7 | } 8 | 9 | MPU_MIN_ALIGN = 8K; 10 | PAGE_SIZE = 4K; 11 | 12 | INCLUDE ../../kernel_layout.ld 13 | 14 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk_b/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nrf52840dk_opensk_b" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | build = "build.rs" 6 | edition = "2018" 7 | 8 | [[bin]] 9 | path = "../nrf52840dk_opensk/src/main.rs" 10 | name = "nrf52840dk_opensk_b" 11 | 12 | [dependencies] 13 | components = { path = "../../components" } 14 | cortexm4 = { path = "../../../arch/cortex-m4" } 15 | capsules = { path = "../../../capsules" } 16 | kernel = { path = "../../../kernel" } 17 | nrf52840 = { path = "../../../chips/nrf52840" } 18 | nrf52_components = { path = "../nrf52_components" } 19 | 20 | [features] 21 | vendor_hid = ["capsules/vendor_hid"] 22 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk_b/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for building the tock kernel for the nRF development kit 2 | 3 | TARGET=thumbv7em-none-eabi 4 | PLATFORM=nrf52840dk_opensk_b 5 | 6 | include ../../Makefile.common 7 | 8 | TOCKLOADER=tockloader 9 | 10 | # Where in the SAM4L flash to load the kernel with `tockloader` 11 | KERNEL_ADDRESS=0x60000 12 | 13 | # Upload programs over uart with tockloader 14 | ifdef PORT 15 | TOCKLOADER_GENERAL_FLAGS += --port $(PORT) 16 | endif 17 | 18 | # Upload the kernel over JTAG 19 | .PHONY: flash 20 | flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin 21 | $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) --board nrf52dk --jlink $< 22 | 23 | # Upload the kernel over JTAG using OpenOCD 24 | .PHONY: flash-openocd 25 | flash-openocd: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin 26 | $(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) --board nrf52dk --openocd $< 27 | 28 | # Upload the kernel over serial/bootloader 29 | .PHONY: program 30 | program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).hex 31 | $(error Cannot program nRF52840DK over USB. Use \`make flash\` and JTAG) 32 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk_b/README.md: -------------------------------------------------------------------------------- 1 | Platform-Specific Instructions: nRF52840-DK, partition B 2 | =================================== 3 | 4 | This is an upgrade partition for the adapted nrf52840dk in `../nrf52840dk_opensk`. 5 | 6 | Compared to our regular board definition for the nrf52840dk, changes are: 7 | - a `layout.ld` with 128 kB for kernel and app 8 | - the matching kernel address in the `Makefile` 9 | - different `StorageLocation`s in `build.rs` 10 | 11 | For everything else, please check the README in `../nrf52840dk_opensk`. 12 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk_b/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::path::Path; 4 | 5 | fn main() { 6 | println!("cargo:rerun-if-changed=layout.ld"); 7 | println!("cargo:rerun-if-changed=../../kernel_layout.ld"); 8 | 9 | let out_dir = env::var_os("OUT_DIR").unwrap(); 10 | let dest_path = Path::new(&out_dir).join("locations.rs"); 11 | fs::write( 12 | &dest_path, 13 | " 14 | static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 5] = [ 15 | // We implement NUM_PAGES = 20 as 16 + 4 to satisfy the MPU. 16 | kernel::StorageLocation { 17 | address: 0xC0000, 18 | size: 0x10000, // 16 pages 19 | storage_type: kernel::StorageType::Store, 20 | }, 21 | kernel::StorageLocation { 22 | address: 0xD0000, 23 | size: 0x4000, // 4 pages 24 | storage_type: kernel::StorageType::Store, 25 | }, 26 | // Partitions can also be split to maximize MPU happiness. 27 | kernel::StorageLocation { 28 | address: 0x4000, 29 | size: 0x2000, 30 | storage_type: kernel::StorageType::Partition, 31 | }, 32 | kernel::StorageLocation { 33 | address: 0x20000, 34 | size: 0x20000, 35 | storage_type: kernel::StorageType::Partition, 36 | }, 37 | kernel::StorageLocation { 38 | address: 0x40000, 39 | size: 0x20000, 40 | storage_type: kernel::StorageType::Partition, 41 | }, 42 | ]; 43 | " 44 | ).unwrap(); 45 | } 46 | -------------------------------------------------------------------------------- /boards/nordic/nrf52840dk_opensk_b/layout.ld: -------------------------------------------------------------------------------- 1 | /* Memory Space Definitions, 1M flash, 256K ram */ 2 | MEMORY 3 | { 4 | rom (rx) : ORIGIN = 0x00060000, LENGTH = 128K 5 | prog (rx) : ORIGIN = 0x00080000, LENGTH = 128K 6 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 256K 7 | } 8 | 9 | MPU_MIN_ALIGN = 8K; 10 | PAGE_SIZE = 4K; 11 | 12 | INCLUDE ../../kernel_layout.ld 13 | 14 | -------------------------------------------------------------------------------- /bootloader/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.thumbv7em-none-eabi] 2 | linker = "arm-none-eabi-gcc" 3 | -------------------------------------------------------------------------------- /bootloader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bootloader" 3 | version = "0.1.0" 4 | authors = [ 5 | "Fabian Kaczmarczyck ", 6 | ] 7 | build = "build.rs" 8 | license = "Apache-2.0" 9 | edition = "2018" 10 | 11 | [dependencies] 12 | byteorder = { version = "1", default-features = false } 13 | cortex-m = "^0.6.0" 14 | cortex-m-rt = "*" 15 | cortex-m-rt-macros = "*" 16 | panic-abort = "0.3.2" 17 | rtt-target = { version = "*", features = ["cortex-m"] } 18 | tock-registers = "0.7.0" 19 | 20 | [profile.dev] 21 | panic = "abort" 22 | lto = true 23 | opt-level = 3 24 | 25 | [profile.release] 26 | panic = "abort" 27 | lto = true 28 | # Level "z" may decrease the binary size more of necessary. 29 | opt-level = 3 30 | -------------------------------------------------------------------------------- /bootloader/README.md: -------------------------------------------------------------------------------- 1 | # OpenSK Bootloader 2 | 3 | This bootloader supports upgradability for OpenSK. Its functionality is to 4 | 5 | - check images on A/B partitions, 6 | - boot the most recent valid partition. 7 | 8 | ## How to use 9 | 10 | The bootloader is built and deployed by OpenSK's `deploy.py`. If your board 11 | defines a metadata address, it is detected as an upgradable board and this 12 | bootloader is flashed to memory address 0. 13 | 14 | ## How to debug 15 | 16 | The bootloader prints debug message over RTT when compiled in debug mode. Using 17 | `nrfjprog` for flashing and inspecting memory is recommended for debugging. 18 | 19 | ```shell 20 | RUSTFLAGS="-C link-arg=-Wl,-Tlink.x -C link-arg=-nostartfiles" \ 21 | cargo build --target thumbv7em-none-eabi 22 | llvm-objcopy -O ihex target/thumbv7em-none-eabi/debug/bootloader \ 23 | target/thumbv7em-none-eabi/debug/bootloader.hex 24 | nrfjprog --program target/thumbv7em-none-eabi/debug/bootloader.hex \ 25 | --sectorerase -f nrf52 --reset 26 | ``` 27 | 28 | To read the debug messages, open two terminals for: 29 | 30 | ```shell 31 | JLinkRTTLogger -device NRF52840_XXAA -if swd -speed 1000 -RTTchannel 0 32 | JLinkRTTClient 33 | ``` 34 | 35 | The first command also logs the output to a file. The second shows all output in 36 | real time. 37 | -------------------------------------------------------------------------------- /bootloader/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | fn main() { 16 | println!("cargo:rerun-if-changed=memory.x"); 17 | } 18 | -------------------------------------------------------------------------------- /bootloader/memory.x: -------------------------------------------------------------------------------- 1 | /* Copyright 2021 Google LLC 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | MEMORY 17 | { 18 | FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16K 19 | RAM (rwx) : ORIGIN = 0x20020000, LENGTH = 128K 20 | } 21 | 22 | -------------------------------------------------------------------------------- /bootloader/src/bitfields.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use tock_registers::register_bitfields; 16 | 17 | register_bitfields! [u32, 18 | // Generic or shared bitfields 19 | pub Task [ 20 | ENABLE OFFSET(0) NUMBITS(1) 21 | ], 22 | 23 | pub Byte [ 24 | VALUE OFFSET(0) NUMBITS(8) 25 | ], 26 | 27 | pub Busy [ 28 | /// Asserted when AES_BUSY or DES_BUSY or HASH_BUSY are asserted or when the DIN FIFO is not empty 29 | BUSY OFFSET(0) NUMBITS(1) [ 30 | Ready = 0, 31 | Busy = 1 32 | ] 33 | ], 34 | 35 | // CC_CTL register bitfields 36 | pub CryptoMode [ 37 | /// Determines the active cryptographic engine 38 | MODE OFFSET(0) NUMBITS(5) [ 39 | Bypass = 0, 40 | Aes = 1, 41 | AesToHash = 2, 42 | AesAndHash = 3, 43 | Des = 4, 44 | DesToHash = 5, 45 | DesAndHash = 6, 46 | Hash = 7, 47 | AesMacAndBypass = 9, 48 | AesToHashAndDout = 10 49 | ] 50 | ], 51 | 52 | // HOST_RGF register bitfields 53 | pub Interrupts [ 54 | /// This interrupt is asserted when all data was delivered to DIN buffer from SRAM 55 | SRAM_TO_DIN OFFSET(4) NUMBITS(1), 56 | /// This interrupt is asserted when all data was delivered to SRAM buffer from DOUT 57 | DOUT_TO_SRAM OFFSET(5) NUMBITS(1), 58 | /// This interrupt is asserted when all data was delivered to DIN buffer from memory 59 | MEM_TO_DIN OFFSET(6) NUMBITS(1), 60 | /// This interrupt is asserted when all data was delivered to memory buffer from DOUT 61 | DOUT_TO_MEM OFFSET(7) NUMBITS(1), 62 | AXI_ERROR OFFSET(8) NUMBITS(1), 63 | /// The PKA end of operation interrupt status 64 | PKA_EXP OFFSET(9) NUMBITS(1), 65 | /// The RNG interrupt status 66 | RNG OFFSET(10) NUMBITS(1), 67 | /// The GPR interrupt status 68 | SYM_DMA_COMPLETED OFFSET(11) NUMBITS(1) 69 | ], 70 | 71 | pub RgfEndianness [ 72 | /// DOUT write endianness 73 | DOUT_WR_BG OFFSET(3) NUMBITS(1) [ 74 | LittleEndian = 0, 75 | BigEndian = 1 76 | ], 77 | /// DIN write endianness 78 | DIN_RD_BG OFFSET(7) NUMBITS(1) [ 79 | LittleEndian = 0, 80 | BigEndian = 1 81 | ], 82 | /// DOUT write word endianness 83 | DOUT_WR_WBG OFFSET(11) NUMBITS(1) [ 84 | LittleEndian = 0, 85 | BigEndian = 1 86 | ], 87 | /// DIN write word endianness 88 | DIN_RD_WBG OFFSET(15) NUMBITS(1) [ 89 | LittleEndian = 0, 90 | BigEndian = 1 91 | ] 92 | ], 93 | 94 | // DIN and DOUT register bitfields 95 | pub LliWord1 [ 96 | /// Total number of bytes to read using DMA in this entry 97 | BYTES_NUM OFFSET(0) NUMBITS(30), 98 | /// Indicates the first LLI entry 99 | FIRST OFFSET(30) NUMBITS(1), 100 | /// Indicates the last LLI entry 101 | LAST OFFSET(31) NUMBITS(1) 102 | ], 103 | 104 | pub HashControl [ 105 | // bit 2 is reserved but to simplify the logic we include it in the bitfield. 106 | MODE OFFSET(0) NUMBITS(4) [ 107 | MD5 = 0, 108 | SHA1 = 1, 109 | SHA256 = 2, 110 | SHA224 = 10 111 | ] 112 | ], 113 | 114 | pub PaddingConfig [ 115 | /// Enable Padding generation. must be reset upon completion of padding. 116 | DO_PAD OFFSET(2) NUMBITS(1) 117 | ] 118 | ]; 119 | -------------------------------------------------------------------------------- /bootloader/src/static_ref.rs: -------------------------------------------------------------------------------- 1 | //! Wrapper type for safe pointers to static memory. 2 | //! 3 | //! Imported from: 4 | //! https://github.com/tock/tock/blob/master/kernel/src/utilities/static_ref.rs 5 | 6 | use core::ops::Deref; 7 | 8 | /// A pointer to statically allocated mutable data such as memory mapped I/O 9 | /// registers. 10 | /// 11 | /// This is a simple wrapper around a raw pointer that encapsulates an unsafe 12 | /// dereference in a safe manner. It serve the role of creating a `&'static T` 13 | /// given a raw address and acts similarly to `extern` definitions, except 14 | /// `StaticRef` is subject to module and crate boundaries, while `extern` 15 | /// definitions can be imported anywhere. 16 | #[derive(Debug)] 17 | pub struct StaticRef { 18 | ptr: *const T, 19 | } 20 | 21 | impl StaticRef { 22 | /// Create a new `StaticRef` from a raw pointer 23 | /// 24 | /// ## Safety 25 | /// 26 | /// Callers must pass in a reference to statically allocated memory which 27 | /// does not overlap with other values. 28 | pub const unsafe fn new(ptr: *const T) -> StaticRef { 29 | StaticRef { ptr } 30 | } 31 | } 32 | 33 | impl Clone for StaticRef { 34 | fn clone(&self) -> Self { 35 | StaticRef { ptr: self.ptr } 36 | } 37 | } 38 | 39 | impl Copy for StaticRef {} 40 | 41 | impl Deref for StaticRef { 42 | type Target = T; 43 | fn deref(&self) -> &T { 44 | unsafe { &*self.ptr } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | extern crate alloc; 16 | 17 | use openssl::{bn, ec, nid}; 18 | use std::fs::File; 19 | use std::io::{Read, Write}; 20 | use std::path::Path; 21 | use std::{env, fs}; 22 | use uuid::Uuid; 23 | 24 | fn main() { 25 | const UPGRADE_FILE: &str = "crypto_data/opensk_upgrade_pub.pem"; 26 | println!("cargo:rerun-if-changed=crypto_data/aaguid.txt"); 27 | println!("cargo:rerun-if-changed={UPGRADE_FILE}"); 28 | println!("cargo:rerun-if-changed=layout.ld"); 29 | println!("cargo:rerun-if-changed=nrf52840_layout.ld"); 30 | println!("cargo:rerun-if-changed=nrf52840_layout_a.ld"); 31 | println!("cargo:rerun-if-changed=nrf52840_layout_b.ld"); 32 | 33 | let out_dir = env::var_os("OUT_DIR").unwrap(); 34 | let aaguid_bin_path = Path::new(&out_dir).join("opensk_aaguid.bin"); 35 | 36 | let mut aaguid_bin_file = File::create(aaguid_bin_path).unwrap(); 37 | let mut aaguid_txt_file = File::open("crypto_data/aaguid.txt").unwrap(); 38 | let mut content = String::new(); 39 | aaguid_txt_file.read_to_string(&mut content).unwrap(); 40 | content.truncate(36); 41 | let aaguid = Uuid::parse_str(&content).unwrap(); 42 | aaguid_bin_file.write_all(aaguid.as_bytes()).unwrap(); 43 | 44 | // COSE encoding the public key, then write it out. 45 | let pem_bytes = fs::read(UPGRADE_FILE).unwrap(); 46 | let ec_key = ec::EcKey::public_key_from_pem(&pem_bytes).ok().unwrap(); 47 | let group = ec::EcGroup::from_curve_name(nid::Nid::X9_62_PRIME256V1).unwrap(); 48 | let conversion_form = ec::PointConversionForm::UNCOMPRESSED; 49 | let mut ctx = bn::BigNumContext::new().unwrap(); 50 | let raw_bytes = ec_key 51 | .public_key() 52 | .to_bytes(&group, conversion_form, &mut ctx) 53 | .unwrap(); 54 | let upgrade_pubkey_path = Path::new(&out_dir).join("opensk_upgrade_pubkey.bin"); 55 | let mut upgrade_pub_bin_file = File::create(upgrade_pubkey_path).unwrap(); 56 | upgrade_pub_bin_file.write_all(&raw_bytes).unwrap(); 57 | } 58 | -------------------------------------------------------------------------------- /docs/FIDO2 Certificate Google FIDO20020210209001.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/OpenSK/ffef5ee5112d2196cfd4243ae76c94c293eda1a0/docs/FIDO2 Certificate Google FIDO20020210209001.pdf -------------------------------------------------------------------------------- /docs/boards/nrf52840_feitian.md: -------------------------------------------------------------------------------- 1 | # OpenSK logo 2 | 3 | ## Feitian OpenSK USB Dongle 4 | 5 | ### Flashing using DFU 6 | 7 | This board is similar in hardware to the Nordic nRF52840 Dongle. You can use DFU 8 | to flash it, instructions to enter DFU mode depend on the version of your 9 | hardware. See 10 | [Feitian's instructions](https://feitiantech.github.io/OpenSK_USB/). In short: 11 | 12 | * In V1, use a paperclip to press the Reset button through the tiny hole. 13 | * In V2, push and hold the user button for more than 10 seconds after 14 | connecting your device. 15 | 16 | Afterwards, you can flash your Feitian OpenSK using DFU following the 17 | [instructions for the Nordic nRF52840 Dongle](nrf52840_dongle.md#Flashing-using-DFU). 18 | 19 | ### Buttons and LEDs 20 | 21 | For both hardware versions, the buttons and LEDs are described in detail in the 22 | [hardware section](https://feitiantech.github.io/OpenSK_USB/hardware/) of 23 | Feitian's website. 24 | -------------------------------------------------------------------------------- /docs/boards/nrf52840_mdk.md: -------------------------------------------------------------------------------- 1 | # OpenSK logo 2 | 3 | ## Nordic nRF52840 MDK 4 | 5 | Makerdiary has instructions on their [website](https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/opensk/). They use a custom script to deploy via DFU. 6 | 7 | After general setup, you still need these steps: 8 | 9 | 1. Create the hexfile with the firmware. 10 | 11 | ```shell 12 | ./deploy.py --board=nrf52840_mdk_dfu --opensk --programmer=none 13 | ``` 14 | 15 | 1. Download the 16 | [script](https://github.com/makerdiary/nrf52840-mdk-usb-dongle/blob/master/tools/uf2conv.py) 17 | from Makerdiary's GitHub into the OpenSK repository. 18 | 19 | 1. Run the script: 20 | 21 | ```shell 22 | py_virtual_env/bin/python3 uf2conv.py -c -f 0xada52840 -o target/opensk.uf2 target/nrf52840_mdk_dfu_merged.hex 23 | ``` 24 | 25 | 1. Boot into DFU mode. Keep the user button pressed on your hardware while 26 | inserting it into a USB slot. You should see a bit of red blinking, and then 27 | a constant green light. 28 | 29 | 1. Your dongle should appear in your normal file browser like other USB sticks. 30 | Copy the file `target/opensk.uf2` over. 31 | 32 | 1. Replug to reboot. 33 | 34 | ### Buttons and LEDs 35 | 36 | The big, white button conveys user presence to the application. Some actions 37 | like register and login will make the device blink, asking you to confirm the 38 | transaction with a button press. 39 | 40 | The LED shows the state of the app. There are different patterns: 41 | 42 | | Pattern | Cause | 43 | |------------------------------------|------------------------| 44 | | red glow | busy | 45 | | red and blue blinking | asking for touch | 46 | | red, green, white pattern for 5s | wink (just saying Hi!) | 47 | | constant green | DFU mode | 48 | -------------------------------------------------------------------------------- /docs/boards/nrf52840dk.md: -------------------------------------------------------------------------------- 1 | # OpenSK logo 2 | 3 | ## Nordic nRF52840-DK board 4 | 5 | ![Nordic development kit](../img/devkit_annotated.jpg) 6 | 7 | ### Flashing using JTAG 8 | 9 | The development board comes with its own JTAG port, so the default programmer 10 | is the easiest and most convenient. You can flash OpenSK with these steps: 11 | 12 | 1. Connect a micro USB cable to the JTAG USB port. 13 | 14 | 1. Run our script for compiling/flashing Tock OS and OpenSK on your device: 15 | 16 | ```shell 17 | ./deploy.py --board=nrf52840dk_opensk --opensk 18 | ``` 19 | 20 | 1. Connect a micro USB cable to the device USB port. 21 | 22 | **Note**: Due to current limitations of our implementation and Tock, you may 23 | have to press the `BOOT/RESET` button, located next to the device USB port on 24 | the board in order to see your OpenSK device on your system. 25 | 26 | ### Buttons and LEDs 27 | 28 | Out of the 5 buttons, the group of 4 behaves identically. They all convey user 29 | presence to the application. Some actions like register and login will make the 30 | board blink, asking you to confirm the transaction with a button press. The 31 | remaining fifth button restarts the board. 32 | 33 | The group of 4 LEDs on the right show the state of the app. There are different 34 | patterns: 35 | 36 | | Pattern | Cause | 37 | |------------------------------------|------------------------| 38 | | LED1 slow blink | kernel panic | 39 | | all LEDs blinking together | app panic | 40 | | LED1+4 and LED2+3 fast alternating | asking for touch | 41 | | fast swirling | wink (just saying Hi!) | 42 | | circle | allocator panic | 43 | 44 | The LEDs closer to the JTAG port indicates the power and debugging state. 45 | 46 | There are 3 switches that need to be in the correct position: 47 | 48 | * Power (bottom left): On 49 | * nRF power source (center left): VDD 50 | * SW6 (top right): DEFAULT 51 | 52 | ### Upgradability 53 | 54 | There are variants of the board that introduce A/B partitions for upgrading the 55 | firmware. You can bootstrap an upgradable board using one of the two commands: 56 | 57 | ```shell 58 | ./deploy.py --board=nrf52840dk_opensk_a --opensk --version=0 59 | ./deploy.py --board=nrf52840dk_opensk_b --opensk --version=0 60 | ``` 61 | 62 | Afterwards, you can upgrade the other partition with 63 | 64 | ```shell 65 | # Board A -> B 66 | ./deploy.py --board=nrf52840dk_opensk_b --opensk --programmer=none --version=1 67 | py_virtual_env/bin/python3 -m tools.deploy_partition --board=nrf52840dk_opensk_b --version=1 68 | # Board B -> A 69 | ./deploy.py --board=nrf52840dk_opensk_a --opensk --programmer=none --version=1 70 | py_virtual_env/bin/python3 -m tools.deploy_partition --board=nrf52840dk_opensk_a --version=1 71 | ``` 72 | 73 | respectively. You can only upgrade the partition that is not currently running, 74 | otherwise your deploy attempts will fail. You can call `deploy_partition` after 75 | you locked down your device, to deploy changes to your development board. 76 | Upgrades only apply after a reboot. 77 | 78 | If you want to use Vendor HID, add the `--vendor-hid` flag to all calls, 79 | for example: 80 | 81 | ```shell 82 | ./deploy.py --board=nrf52840dk_opensk_a --opensk --version=0 --vendor-hid 83 | ./deploy.py --board=nrf52840dk_opensk_b --opensk --programmer=none --version=1 --vendor-hid 84 | py_virtual_env/bin/python3 -m tools.deploy_partition --board=nrf52840dk_opensk_b --version=1 --vendor-hid 85 | ``` 86 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google/conduct/). 29 | -------------------------------------------------------------------------------- /docs/customization.md: -------------------------------------------------------------------------------- 1 | # OpenSK logo 2 | 3 | ## Customization 4 | 5 | ### Cryptographic material 6 | 7 | All the generated certificates and private keys are stored in the directory 8 | `crypto_data/`. The expected content after running our `setup.sh` script is: 9 | 10 | | File | Purpose | 11 | | ------------------------ | ----------------------------------------------- | 12 | | `aaguid.txt` | Text file containaing the AAGUID value | 13 | | `opensk_ca.csr` | Certificate sign request for the Root CA | 14 | | `opensk_ca.key` | ECC secp256r1 private key used for the Root CA | 15 | | `opensk_ca.pem` | PEM encoded certificate of the Root CA | 16 | | `opensk_ca.srl` | File generated by OpenSSL | 17 | | `opensk_cert.csr` | CSR for attestation certificate | 18 | | `opensk_cert.pem` | PEM encoded certificate for the authenticator | 19 | | `opensk.key` | ECC secp256r1 private key for the autenticator | 20 | | `opensk_upgrade.key` | Private key for signing upgrades through CTAP | 21 | | `opensk_upgrade_pub.pem` | Public key for verifying upgrades | 22 | 23 | If you want to use your own attestation certificate and private key, 24 | replace the `opensk_cert.pem` and `opensk.key` files. The script at 25 | `tools/configure.py` customizes an OpenSK device with the correct certificate 26 | and private key. 27 | 28 | Our build script `build.rs` is responsible for converting the `aaguid.txt` file 29 | into raw data that is then used by the Rust file `src/ctap/key_material.rs`. 30 | 31 | Please make sure to safely store all private key material before calling 32 | `reset.sh`, or the files will be lost. 33 | 34 | #### Certificate considerations 35 | 36 | The certificate on OpenSK is used for attestation. That means, whenever you 37 | register OpenSK on a website, you attest the legitimacy of your hardware. For 38 | self-generated certificates, this claim is rather trivial. Still, it is required 39 | by some websites and to use U2F. 40 | 41 | Usually, the attestation private key is shared between a batch of at least 42 | 100,000 security keys of the same model. If you build your own OpenSK, your 43 | private key is unique to you. This makes you identifiable across registrations: 44 | Two websites could collaborate to track if registrations were attested with the 45 | same key material. If you use OpenSK beyond experimentation, please consider 46 | carefully if you want to take this privacy risk. 47 | 48 | ### Software personalization 49 | 50 | If you build your own security key, depending on the hardware you use, there are 51 | a few things you can personalize: 52 | 53 | 1. If you have multiple buttons, choose the buttons responsible for user 54 | presence in `src/main.rs`. 55 | 1. If you have colored LEDs, like different blinking patterns and want to play 56 | around with the code in `src/main.rs` more, take a look at e.g. `wink_leds`. 57 | 1. You find more options and documentation in `src/ctap/customization.rs`, 58 | including: 59 | * The default level for the credProtect extension. 60 | * The default minimum PIN length, and what relying parties can set it. 61 | * Whether you want to enforce alwaysUv. 62 | * Settings for enterprise attestation. 63 | * The maximum PIN retries. 64 | * Whether you want to use batch attestation. 65 | * Whether you want to use signature counters. 66 | * Various constants to adapt to different hardware. 67 | 68 | ### Testing and Fuzzing 69 | 70 | You might want to test your changes before deploying them. To run unit tests, 71 | make sure that at least the `std` feature is included, e.g.: 72 | 73 | ```shell 74 | cargo test --features=std,with_ctap1 75 | ``` 76 | 77 | Alternatively, you can simply call our test script to also test all libraries, 78 | run clippy, check formatting and more: 79 | 80 | ```shell 81 | ./run_desktop_tests.sh 82 | ``` 83 | 84 | OpenSK is fuzzed with the [OSS-Fuzz](https://github.com/google/oss-fuzz) 85 | project. You can also run fuzzing locally. First install: 86 | 87 | ```shell 88 | ./fuzzing_setup.sh 89 | ``` 90 | 91 | Then choose a fuzz target from `fuzz/fuzz_targets/`, e.g.: 92 | 93 | ```shell 94 | cargo fuzz run fuzz_target_process_ctap1 95 | ``` 96 | -------------------------------------------------------------------------------- /docs/debugging.md: -------------------------------------------------------------------------------- 1 | # OpenSK logo 2 | 3 | ## Troubleshooting and Debugging 4 | 5 | ### Inspecting USB 6 | 7 | The following commands should help you identify whether your operating system 8 | identifies OpenSK over USB. 9 | 10 | #### Linux 11 | 12 | When plugging in the USB key, the following line should appear in `lsusb`. 13 | 14 | ```shell 15 | $ lsusb 16 | ... 17 | Bus XXX Device YYY: ID 1915:521f Nordic Semiconductor ASA OpenSK 18 | ``` 19 | 20 | You should also see lines similar to the following in `dmesg`. 21 | 22 | ```shell 23 | $ dmesg 24 | ... 25 | [XXX] usb A-BB: new full-speed USB device number 00 using xhci_hcd 26 | [XXX] usb A-BB: New USB device found, idVendor=1915, idProduct=521f, bcdDevice= 0.01 27 | [XXX] usb A-BB: New USB device strings: Mfr=1, Product=2, SerialNumber=3 28 | [XXX] usb A-BB: Product: OpenSK 29 | [XXX] usb A-BB: Manufacturer: Nordic Semiconductor ASA 30 | [XXX] usb A-BB: SerialNumber: v0.1 31 | [XXX] hid-generic 0000:0000:0000.0000: hiddev0,hidraw0: USB HID v1.10 Device [Nordic Semiconductor ASA OpenSK] on usb-0000:00:00.0-00/input0 32 | ``` 33 | 34 | #### Mac OS X 35 | 36 | When plugging in the USB key, you should see a similar line by using the `ioreg` 37 | tool: 38 | 39 | ```shell 40 | $ ioreg -p IOUSB 41 | +-o Root 42 | ... 43 | +-o AppleUSBXHCI Root Hub Simulation@14000000 44 | +-o OpenSK@14400000 45 | ``` 46 | 47 | ### Debug console 48 | 49 | On the dev board, you can read the debug messages using JLink. Use one terminal 50 | for the server and one for the client: 51 | 52 | ```shell 53 | # Terminal 1 54 | JLinkExe -device nrf52 -if swd -speed 1000 -autoconnect 1 55 | # Terminal 2 56 | JLinkRTTClient 57 | ``` 58 | 59 | You can enhance the debug output by adding flags to the deploy command (see 60 | below for details): 61 | 62 | * `--debug`: more debug messages 63 | * `--panic-console`: add panic messages 64 | * `--debug-allocations`: print information about the used heap 65 | 66 | Adding debugging to your firmware increases resource usage, including 67 | 68 | * USB communication speed 69 | * RAM usage 70 | * binary size 71 | 72 | Depending on your choice of board, you may have to increase the available stack 73 | for kernel or app, or disable features so that the binary fits the flash. Also 74 | expect more packet loss. 75 | 76 | ### App panic messages 77 | 78 | By default, libtock-rs blinks some LEDs when the userspace application panics. 79 | This is not always convenient as the panic message is lost. In order to enable 80 | a custom panic handler that first writes the panic message via Tock's console 81 | driver, before faulting the app, you can use the `--panic-console` flag of the 82 | `deploy.py` script. 83 | 84 | ```shell 85 | # Example on Nordic nRF52840-DK board 86 | ./deploy.py --board=nrf52840dk_opensk --opensk --panic-console 87 | ``` 88 | 89 | ### Memory allocations 90 | 91 | You may want to track memory allocations to understand the heap usage of 92 | OpenSK. This can be useful if you plan to port it to a board with fewer 93 | available RAM for example. To do so, you can enable the `--debug-allocations` 94 | flag of the `deploy.py` script. This enables a custom (userspace) allocator 95 | that prints a message to the console for each allocation and deallocation 96 | operation. 97 | 98 | The additional output looks like the following. 99 | 100 | ```text 101 | # Allocation of 256 byte(s), aligned on 1 byte(s). The allocated address is 102 | # 0x2002401c. After this operation, 2 pointers have been allocated, totalling 103 | # 384 bytes (the total heap usage may be larger, due to alignment and 104 | # fragmentation of allocations within the heap). 105 | alloc[256, 1] = 0x2002401c (2 ptrs, 384 bytes) 106 | # Deallocation of 64 byte(s), aligned on 1 byte(s), from address 0x2002410c. 107 | # After this operation, 1 pointers are allocated, totalling 512 bytes. 108 | dealloc[64, 1] = 0x2002410c (1 ptrs, 512 bytes) 109 | ``` 110 | -------------------------------------------------------------------------------- /docs/img/FIDO2_Certified_L1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/OpenSK/ffef5ee5112d2196cfd4243ae76c94c293eda1a0/docs/img/FIDO2_Certified_L1.png -------------------------------------------------------------------------------- /docs/img/OpenSK.svg: -------------------------------------------------------------------------------- 1 | Mono -------------------------------------------------------------------------------- /docs/img/devkit_annotated.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/OpenSK/ffef5ee5112d2196cfd4243ae76c94c293eda1a0/docs/img/devkit_annotated.jpg -------------------------------------------------------------------------------- /docs/img/dongle_clip.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/OpenSK/ffef5ee5112d2196cfd4243ae76c94c293eda1a0/docs/img/dongle_clip.jpg -------------------------------------------------------------------------------- /docs/img/dongle_front.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/OpenSK/ffef5ee5112d2196cfd4243ae76c94c293eda1a0/docs/img/dongle_front.jpg -------------------------------------------------------------------------------- /docs/img/dongle_pads.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/OpenSK/ffef5ee5112d2196cfd4243ae76c94c293eda1a0/docs/img/dongle_pads.jpg -------------------------------------------------------------------------------- /docs/img/enclosure.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/OpenSK/ffef5ee5112d2196cfd4243ae76c94c293eda1a0/docs/img/enclosure.jpg -------------------------------------------------------------------------------- /examples/console_test.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![no_main] 16 | #![no_std] 17 | 18 | extern crate lang_items; 19 | 20 | use libtock_console::Console; 21 | use libtock_drivers::result::FlexUnwrap; 22 | use libtock_runtime::{set_main, stack_size, TockSyscalls}; 23 | 24 | stack_size! {0x800} 25 | set_main! {main} 26 | 27 | type Syscalls = TockSyscalls; 28 | 29 | fn main() { 30 | // Write messages of length up to the console driver's buffer size. 31 | let mut buf = [0; 1024]; 32 | loop { 33 | for i in 1..buf.len() { 34 | for byte in buf.iter_mut().take(i) { 35 | *byte = b'0' + ((i % 10) as u8); 36 | } 37 | buf[i] = b'\n'; 38 | Console::::write(&buf[..(i + 1)]) 39 | .map_err(|e| e.into()) 40 | .flex_unwrap(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /examples/erase_storage.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![no_main] 16 | #![no_std] 17 | 18 | extern crate lang_items; 19 | 20 | use core::fmt::Write; 21 | use ctap2::env::tock::take_storage; 22 | use libtock_console::Console; 23 | use libtock_drivers::result::FlexUnwrap; 24 | use libtock_leds::Leds; 25 | use libtock_platform as platform; 26 | use libtock_runtime::{set_main, stack_size, TockSyscalls}; 27 | use persistent_store::{Storage, StorageIndex}; 28 | use platform::DefaultConfig; 29 | 30 | stack_size! {0x800} 31 | set_main! {main} 32 | 33 | type Syscalls = TockSyscalls; 34 | 35 | fn is_page_erased(storage: &dyn Storage, page: usize) -> bool { 36 | let index = StorageIndex { page, byte: 0 }; 37 | let length = storage.page_size(); 38 | storage 39 | .read_slice(index, length) 40 | .unwrap() 41 | .iter() 42 | .all(|&x| x == 0xff) 43 | } 44 | 45 | fn main() { 46 | Leds::::on(1).map_err(|e| e.into()).flex_unwrap(); // red on dongle 47 | let mut storage = take_storage::().unwrap(); 48 | let num_pages = storage.num_pages(); 49 | let mut console = Console::::writer(); 50 | writeln!(console, "Erase {} pages of storage:", num_pages).unwrap(); 51 | for page in 0..num_pages { 52 | write!(console, "- Page {} ", page).unwrap(); 53 | if is_page_erased(&storage, page) { 54 | writeln!(console, "skipped (was already erased).").unwrap(); 55 | } else { 56 | storage.erase_page(page).unwrap(); 57 | writeln!(console, "erased.").unwrap(); 58 | } 59 | } 60 | writeln!(console, "Done.").unwrap(); 61 | Leds::::on(1).map_err(|e| e.into()).flex_unwrap(); 62 | Leds::::off(0).map_err(|e| e.into()).flex_unwrap(); // green on dongle 63 | } 64 | -------------------------------------------------------------------------------- /examples/oom_test.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![no_main] 16 | #![no_std] 17 | 18 | extern crate alloc; 19 | extern crate lang_items; 20 | 21 | use alloc::vec::Vec; 22 | use core::fmt::Write; 23 | use libtock_console::Console; 24 | use libtock_runtime::{set_main, stack_size, TockSyscalls}; 25 | 26 | stack_size! {0x800} 27 | set_main! {main} 28 | 29 | type Syscalls = TockSyscalls; 30 | 31 | fn main() { 32 | let mut console = Console::::writer(); 33 | writeln!(console, "****************************************").unwrap(); 34 | for i in 0.. { 35 | writeln!(console, "Allocating {} bytes...", 1 << i).unwrap(); 36 | let x: Vec = Vec::with_capacity(1 << i); 37 | writeln!(console, "Allocated!").unwrap(); 38 | drop(x); 39 | writeln!(console, "Dropped!").unwrap(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/panic_test.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![no_main] 16 | #![no_std] 17 | 18 | extern crate lang_items; 19 | 20 | use libtock_runtime::{set_main, stack_size}; 21 | 22 | stack_size! {0x800} 23 | set_main! {main} 24 | 25 | fn main() { 26 | panic!("Bye world!") 27 | } 28 | -------------------------------------------------------------------------------- /fuzzing_setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2019 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # Ensure the script doesn't fail on Github workflows 17 | export TERM=${TERM:-vt100} 18 | done_text="$(tput bold)DONE.$(tput sgr0)" 19 | 20 | set -e 21 | 22 | # Install cargo-fuzz library. 23 | cargo +stable install cargo-fuzz 24 | -------------------------------------------------------------------------------- /libraries/cbor/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "sk-cbor" 7 | version = "0.1.2" 8 | -------------------------------------------------------------------------------- /libraries/cbor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sk-cbor" 3 | version = "0.1.2" 4 | authors = [ 5 | "Fabian Kaczmarczyck ", 6 | "Guillaume Endignoux ", 7 | "Jean-Michel Picod ", 8 | "David Drysdale ", 9 | ] 10 | license = "Apache-2.0" 11 | edition = "2018" 12 | description = "CBOR parsing library" 13 | homepage = "https://github.com/google/OpenSK" 14 | repository = "https://github.com/google/OpenSK" 15 | keywords = ["cbor", "serialization", "no_std"] 16 | categories = ["encoding"] 17 | readme = "README.md" 18 | 19 | [badges] 20 | maintenance = { status = "passively-maintained" } 21 | 22 | [dependencies] 23 | -------------------------------------------------------------------------------- /libraries/cbor/README.md: -------------------------------------------------------------------------------- 1 | # CBOR Parsing Library 2 | 3 | [![crates.io](https://img.shields.io/crates/d/sk-cbor.svg)](https://crates.io/crates/sk-cbor) 4 | [![crates.io](https://img.shields.io/crates/v/sk-cbor.svg)](https://crates.io/crates/sk-cbor) 5 | [![docs.rs](https://docs.rs/sk-cbor/badge.svg)](https://docs.rs/sk-cbor) 6 | [![License](https://img.shields.io/crates/l/sk-cbor.svg)](https://crates.io/crates/sk-cbor) 7 | [![Maintenance](https://img.shields.io/maintenance/yes/2021)](https://crates.io/crates/sk-cbor) 8 | 9 | This crate implements Concise Binary Object Representation (CBOR) from [RFC 10 | 8949](https://datatracker.ietf.org/doc/html/rfc8949). 11 | 12 | ## Usage 13 | 14 | ```rust 15 | fn main() { 16 | // Build a CBOR object with the crate's convenience macros. Note that this 17 | // object is not built in canonical order. 18 | let map_object = cbor_map! { 19 | 1 => cbor_array![2, 3], 20 | "tstr" => cbor_bytes!(vec![1, 2, 3]), 21 | -2 => cbor_null!(), 22 | 3 => cbor_true!(), 23 | }; 24 | 25 | println!("Object {:?}", map_object); 26 | 27 | // Serialize to bytes. 28 | let mut map_data = vec![]; 29 | sk_cbor::writer::write(map_object, &mut map_data).unwrap(); 30 | let hex_map_data = hex::encode(&map_data); 31 | 32 | // Serialized version is in canonical order. 33 | println!("Serializes to {}", hex_map_data); 34 | assert_eq!( 35 | hex_map_data, 36 | concat!( 37 | "a4", // 4-map 38 | "01", // int(1) => 39 | "820203", // 2-array [2, 3], 40 | "03", // int(3) => 41 | "f5", // true, 42 | "21", // nint(-2) => 43 | "f6", // null, 44 | "6474737472", // 4-tstr "tstr" => 45 | "43010203" // 3-bstr 46 | ) 47 | ); 48 | 49 | // Convert back to an object. This is different than the original object, 50 | // because the map is now in canonical order. 51 | let recovered_object = sk_cbor::reader::read(&map_data).unwrap(); 52 | println!("Deserializes to {:?}", recovered_object); 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /libraries/cbor/examples/cbor.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | //! Example program demonstrating cbor usage. 18 | 19 | extern crate alloc; 20 | 21 | use sk_cbor::values::Value; 22 | use sk_cbor::{cbor_array, cbor_bytes, cbor_map, cbor_null, cbor_true}; 23 | 24 | fn hexify(data: &[u8]) -> String { 25 | let mut s = String::new(); 26 | for b in data { 27 | s.push_str(&format!("{:02x}", b)); 28 | } 29 | s 30 | } 31 | 32 | fn main() { 33 | // Build a CBOR object with various different types included. Note that this 34 | // object is not built in canonical order. 35 | let manual_object = Value::map(vec![ 36 | ( 37 | Value::from(1), 38 | Value::array(vec![Value::from(2), Value::from(3)]), 39 | ), 40 | (Value::from("tstr".to_owned()), Value::from(vec![1, 2, 3])), 41 | (Value::from(-2), Value::null_value()), 42 | (Value::from(3), Value::bool_value(true)), 43 | ]); 44 | 45 | // Build the same object using the crate's convenience macros. 46 | let macro_object = cbor_map! { 47 | 1 => cbor_array![2, 3], 48 | "tstr" => cbor_bytes!(vec![1, 2, 3]), 49 | -2 => cbor_null!(), 50 | 3 => cbor_true!(), 51 | }; 52 | 53 | assert_eq!(manual_object, macro_object); 54 | println!("Object {:?}", manual_object); 55 | 56 | // Serialize to bytes. 57 | let mut manual_data = vec![]; 58 | sk_cbor::writer::write(manual_object, &mut manual_data).unwrap(); 59 | let hex_manual_data = hexify(&manual_data); 60 | let mut macro_data = vec![]; 61 | sk_cbor::writer::write(macro_object, &mut macro_data).unwrap(); 62 | let hex_macro_data = hexify(¯o_data); 63 | 64 | assert_eq!(hex_manual_data, hex_macro_data); 65 | 66 | // Serialized version is in canonical order. 67 | println!("Serializes to {}", hex_manual_data); 68 | assert_eq!( 69 | hex_manual_data, 70 | concat!( 71 | "a4", // 4-map 72 | "01", // int(1) => 73 | "820203", // 2-array [2, 3], 74 | "03", // int(3) => 75 | "f5", // true, 76 | "21", // nint(-2) => 77 | "f6", // null, 78 | "6474737472", // 4-tstr "tstr" => 79 | "43010203" // 3-bstr 80 | ) 81 | ); 82 | 83 | // Convert back to an object. This is different than the original object, 84 | // because the map is now in canonical order. 85 | let recovered_object = sk_cbor::reader::read(&manual_data).unwrap(); 86 | println!("Deserializes to {:?}", recovered_object); 87 | } 88 | -------------------------------------------------------------------------------- /libraries/cbor/fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | /artifacts/ 2 | /corpus/ 3 | /target/ 4 | -------------------------------------------------------------------------------- /libraries/cbor/fuzz/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "arbitrary" 7 | version = "0.4.7" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "db55d72333851e17d572bec876e390cd3b11eb1ef53ae821dd9f3b653d2b4569" 10 | 11 | [[package]] 12 | name = "cc" 13 | version = "1.2.25" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" 16 | dependencies = [ 17 | "shlex", 18 | ] 19 | 20 | [[package]] 21 | name = "libfuzzer-sys" 22 | version = "0.3.5" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "fcf184a4b6b274f82a5df6b357da6055d3e82272327bba281c28bbba6f1664ef" 25 | dependencies = [ 26 | "arbitrary", 27 | "cc", 28 | ] 29 | 30 | [[package]] 31 | name = "shlex" 32 | version = "1.3.0" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 35 | 36 | [[package]] 37 | name = "sk-cbor" 38 | version = "0.1.2" 39 | 40 | [[package]] 41 | name = "sk-cbor-fuzz" 42 | version = "0.0.0" 43 | dependencies = [ 44 | "libfuzzer-sys", 45 | "sk-cbor", 46 | ] 47 | -------------------------------------------------------------------------------- /libraries/cbor/fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "sk-cbor-fuzz" 4 | version = "0.0.0" 5 | authors = ["Automatically generated"] 6 | publish = false 7 | edition = "2018" 8 | 9 | [package.metadata] 10 | cargo-fuzz = true 11 | 12 | [dependencies] 13 | libfuzzer-sys = "0.3" 14 | 15 | [dependencies.sk-cbor] 16 | path = ".." 17 | 18 | # Prevent this from interfering with workspaces 19 | [workspace] 20 | members = ["."] 21 | 22 | [[bin]] 23 | name = "fuzz_target_cbor" 24 | path = "fuzz_targets/fuzz_target_cbor.rs" 25 | test = false 26 | doc = false 27 | -------------------------------------------------------------------------------- /libraries/cbor/fuzz/fuzz_targets/fuzz_target_cbor.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | extern crate alloc; 3 | 4 | use alloc::vec::Vec; 5 | use libfuzzer_sys::fuzz_target; 6 | use sk_cbor as cbor; 7 | 8 | fuzz_target!(|data: &[u8]| { 9 | if let Ok(value) = cbor::read(data) { 10 | let mut result = Vec::new(); 11 | assert!(cbor::write(value, &mut result).is_ok()); 12 | assert_eq!(result, data); 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /libraries/cbor/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![no_std] 16 | 17 | extern crate alloc; 18 | 19 | pub mod macros; 20 | pub mod reader; 21 | pub mod values; 22 | pub mod writer; 23 | 24 | pub use self::reader::read; 25 | pub use self::values::Value; 26 | pub use self::writer::write; 27 | -------------------------------------------------------------------------------- /libraries/opensk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "opensk" 3 | version = "1.0.0" 4 | authors = [ 5 | "Fabian Kaczmarczyck ", 6 | "Guillaume Endignoux ", 7 | "Jean-Michel Picod ", 8 | "Julien Cretin ", 9 | ] 10 | license = "Apache-2.0" 11 | edition = "2018" 12 | rust-version = "1.66" 13 | 14 | [dependencies] 15 | sk-cbor = { path = "../cbor" } 16 | persistent_store = { path = "../persistent_store" } 17 | byteorder = { version = "1", default-features = false } 18 | arrayref = "0.3.6" 19 | subtle = { version = "2.2", default-features = false, features = ["nightly"] } 20 | arbitrary = { version = "0.4.7", features = ["derive"], optional = true } 21 | ed25519-compact = { version = "1", default-features = false, optional = true } 22 | rand_core = "0.6.4" 23 | rand = { version = "0.8.5", default-features = false, optional = true } 24 | sha2 = { version = "0.10.6", default-features = false } 25 | hmac = { version = "0.12.1", default-features = false } 26 | hkdf = { version = "0.12.3", default-features = false } 27 | aes = { version = "0.8.2", default-features = false } 28 | cbc = { version = "0.1.2", default-features = false } 29 | zeroize = { version = "1.5.7", features = ["derive"] } 30 | 31 | [dependencies.p256] 32 | version = "0.13.0" 33 | default-features = false 34 | features = ["alloc", "ecdh", "ecdsa"] 35 | 36 | [features] 37 | default = ["config_command", "with_ctap1"] 38 | config_command = [] 39 | debug_ctap = [] 40 | std = ["persistent_store/std", "rand/std_rng", "config_command"] 41 | with_ctap1 = [] 42 | vendor_hid = [] 43 | fuzz = ["arbitrary", "std"] 44 | ed25519 = ["ed25519-compact"] 45 | 46 | [dev-dependencies] 47 | enum-iterator = "0.6.0" 48 | 49 | [build-dependencies] 50 | sk-cbor = { path = "../cbor" } 51 | uuid = { version = "0.8", features = ["v4"] } 52 | openssl = "0.10.36" 53 | -------------------------------------------------------------------------------- /libraries/opensk/fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | /artifacts/ 2 | /corpus/ 3 | /target/ 4 | -------------------------------------------------------------------------------- /libraries/opensk/fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "opensk-fuzz" 3 | version = "0.0.0" 4 | authors = ["Automatically generated"] 5 | publish = false 6 | edition = "2018" 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [dependencies] 12 | libfuzzer-sys = { version = "0.3" } 13 | fuzz_helper = { path = "fuzz_helper" } 14 | 15 | # Prevent this from interfering with workspaces 16 | [workspace] 17 | members = ["."] 18 | 19 | [[bin]] 20 | name = "fuzz_target_process_ctap_command" 21 | path = "fuzz_targets/fuzz_target_process_ctap_command.rs" 22 | test = false 23 | doc = false 24 | 25 | [[bin]] 26 | name = "fuzz_target_process_ctap1" 27 | path = "fuzz_targets/fuzz_target_process_ctap1.rs" 28 | test = false 29 | doc = false 30 | 31 | [[bin]] 32 | name = "fuzz_target_process_ctap2_client_pin" 33 | path = "fuzz_targets/fuzz_target_process_ctap2_client_pin.rs" 34 | test = false 35 | doc = false 36 | 37 | [[bin]] 38 | name = "fuzz_target_process_ctap2_client_pin_structured" 39 | path = "fuzz_targets/fuzz_target_process_ctap2_client_pin_structured.rs" 40 | test = false 41 | doc = false 42 | 43 | [[bin]] 44 | name = "fuzz_target_process_ctap2_get_assertion" 45 | path = "fuzz_targets/fuzz_target_process_ctap2_get_assertion.rs" 46 | test = false 47 | doc = false 48 | 49 | [[bin]] 50 | name = "fuzz_target_process_ctap2_get_assertion_structured" 51 | path = "fuzz_targets/fuzz_target_process_ctap2_get_assertion_structured.rs" 52 | test = false 53 | doc = false 54 | 55 | [[bin]] 56 | name = "fuzz_target_process_ctap2_make_credential" 57 | path = "fuzz_targets/fuzz_target_process_ctap2_make_credential.rs" 58 | test = false 59 | doc = false 60 | 61 | [[bin]] 62 | name = "fuzz_target_process_ctap2_make_credential_structured" 63 | path = "fuzz_targets/fuzz_target_process_ctap2_make_credential_structured.rs" 64 | test = false 65 | doc = false 66 | 67 | [[bin]] 68 | name = "fuzz_target_split_assemble" 69 | path = "fuzz_targets/fuzz_target_split_assemble.rs" 70 | test = false 71 | doc = false 72 | -------------------------------------------------------------------------------- /libraries/opensk/fuzz/fuzz_helper/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fuzz_helper" 3 | version = "0.1.0" 4 | authors = ["Mingxiao Guo "] 5 | license = "Apache-2.0" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | arrayref = "0.3.6" 10 | opensk = { path = "../..", features = ["fuzz"] } 11 | sk-cbor = { path = "../../../cbor" } 12 | arbitrary = { version = "0.4.7", features = ["derive"] } 13 | -------------------------------------------------------------------------------- /libraries/opensk/fuzz/fuzz_targets/fuzz_target_process_ctap1.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzz_helper::{process_ctap_specific_type, InputType}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | // Fuzz inputs as CTAP1 U2F raw messages. 7 | // For a more generic fuzz target including all CTAP commands, you can use 8 | // fuzz_target_process_ctap_command. 9 | fuzz_target!(|data: &[u8]| { 10 | process_ctap_specific_type(data, InputType::Ctap1).ok(); 11 | }); 12 | -------------------------------------------------------------------------------- /libraries/opensk/fuzz/fuzz_targets/fuzz_target_process_ctap2_client_pin.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzz_helper::{process_ctap_specific_type, InputType}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | // Fuzz inputs as CTAP2 client pin command parameters encoded in cbor. 7 | // For a more generic fuzz target including all CTAP commands, you can use 8 | // fuzz_target_process_ctap_command. 9 | fuzz_target!(|data: &[u8]| { 10 | process_ctap_specific_type(data, InputType::CborClientPinParameter).ok(); 11 | }); 12 | -------------------------------------------------------------------------------- /libraries/opensk/fuzz/fuzz_targets/fuzz_target_process_ctap2_client_pin_structured.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzz_helper::{process_ctap_structured, InputType}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | // Fuzz inputs as CTAP2 client pin command parameters. 7 | // The inputs will used to construct arbitrary client pin parameters. 8 | fuzz_target!(|data: &[u8]| { 9 | process_ctap_structured(data, InputType::CborClientPinParameter).ok(); 10 | }); 11 | -------------------------------------------------------------------------------- /libraries/opensk/fuzz/fuzz_targets/fuzz_target_process_ctap2_get_assertion.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzz_helper::{process_ctap_specific_type, InputType}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | // Fuzz inputs as CTAP2 get assertion command parameters encoded in cbor. 7 | // For a more generic fuzz target including all CTAP commands, you can use 8 | // fuzz_target_process_ctap_command. 9 | fuzz_target!(|data: &[u8]| { 10 | process_ctap_specific_type(data, InputType::CborGetAssertionParameter).ok(); 11 | }); 12 | -------------------------------------------------------------------------------- /libraries/opensk/fuzz/fuzz_targets/fuzz_target_process_ctap2_get_assertion_structured.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzz_helper::{process_ctap_structured, InputType}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | // Fuzz inputs as CTAP2 get assertion command parameters. 7 | // The inputs will used to construct arbitrary get assertion parameters. 8 | fuzz_target!(|data: &[u8]| { 9 | process_ctap_structured(data, InputType::CborGetAssertionParameter).ok(); 10 | }); 11 | -------------------------------------------------------------------------------- /libraries/opensk/fuzz/fuzz_targets/fuzz_target_process_ctap2_make_credential.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzz_helper::{process_ctap_specific_type, InputType}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | // Fuzz inputs as CTAP2 make credential command parameters encoded in cbor. 7 | // For a more generic fuzz target including all CTAP commands, you can use 8 | // fuzz_target_process_ctap_command. 9 | fuzz_target!(|data: &[u8]| { 10 | process_ctap_specific_type(data, InputType::CborMakeCredentialParameter).ok(); 11 | }); 12 | -------------------------------------------------------------------------------- /libraries/opensk/fuzz/fuzz_targets/fuzz_target_process_ctap2_make_credential_structured.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzz_helper::{process_ctap_structured, InputType}; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | // Fuzz inputs as CTAP2 make credential command parameters. 7 | // The inputs will used to construct arbitrary make credential parameters. 8 | fuzz_target!(|data: &[u8]| { 9 | process_ctap_structured(data, InputType::CborMakeCredentialParameter).ok(); 10 | }); 11 | -------------------------------------------------------------------------------- /libraries/opensk/fuzz/fuzz_targets/fuzz_target_process_ctap_command.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzz_helper::process_ctap_any_type; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | // Generically fuzz inputs as CTAP commands. 7 | fuzz_target!(|data: &[u8]| { 8 | process_ctap_any_type(data).ok(); 9 | }); 10 | -------------------------------------------------------------------------------- /libraries/opensk/fuzz/fuzz_targets/fuzz_target_split_assemble.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use fuzz_helper::split_assemble_hid_packets; 4 | use libfuzzer_sys::fuzz_target; 5 | 6 | // Fuzzing HID packets splitting and assembling functions. 7 | fuzz_target!(|data: &[u8]| { 8 | split_assemble_hid_packets(data).ok(); 9 | }); 10 | -------------------------------------------------------------------------------- /libraries/opensk/fuzz/make_corpus.py: -------------------------------------------------------------------------------- 1 | """Creates a directory containing seed inputs from a json file having 2 | the following structure: 3 | [ 4 | { 5 | "hex": "a901a1182a182a02a3626964781a6d616b655f6261645f7...", 6 | "cbor": "{1: h'42', 2: {\"id\": \"make.example.com\", ...", 7 | "description": "make credential parameters" 8 | }, 9 | ... 10 | ] 11 | 12 | Usage: 13 | - pass the resulting corpus directory path as the first argument 14 | - pass the json file path to make the corpus from as the second argument 15 | Example: 16 | python make_corpus.py ./corpus ./corpus_file.json 17 | """ 18 | import argparse 19 | import json 20 | import os.path 21 | 22 | 23 | # Creates a corpus directory to the given path from the given json file. 24 | def make_corpus(corpus_dir, corpus_json): 25 | if not os.path.exists(corpus_dir): 26 | os.makedirs(corpus_dir) 27 | elif not os.path.isdir(corpus_dir): 28 | raise NotADirectoryError 29 | 30 | if os.path.isfile(corpus_json) and \ 31 | os.path.splitext(corpus_json)[-1] == ".json": 32 | with open(corpus_json, encoding="utf-8") as corpus_file: 33 | corpus = json.load(corpus_file) 34 | else: 35 | raise TypeError 36 | 37 | for i, seed_file in enumerate(corpus): 38 | seed_file_name = "seed_file_" + str(i) 39 | raw_hex = seed_file["hex"].decode("hex") 40 | with open(os.path.join(corpus_dir, seed_file_name), "wb") as f: 41 | f.write(raw_hex) 42 | 43 | 44 | def main(): 45 | parser = argparse.ArgumentParser() 46 | parser.add_argument( 47 | "corpus_directory", help="the resulting corpus directory path") 48 | parser.add_argument( 49 | "corpus_json", help="the json file path to make the corpus from") 50 | args = parser.parse_args() 51 | try: 52 | make_corpus(args.corpus_directory, args.corpus_json) 53 | except NotADirectoryError: 54 | print(args.corpus_directory, " is not a directory.\n") 55 | except TypeError: 56 | print(args.corpus_json, " must be a json file.\n") 57 | 58 | 59 | if __name__ == "__main__": 60 | main() 61 | -------------------------------------------------------------------------------- /libraries/opensk/src/api/clock.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022-2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | pub trait Clock { 16 | /// Stores data for the clock to recognize if this timer is elapsed or not. 17 | /// 18 | /// The Clock does not keep track of the timers it creates. Therefore, they should not wrap 19 | /// unexpectedly. A timer that is elapsed may never return to a non-elapsed state. 20 | /// 21 | /// A default Timer should return `true` when checked with `is_elapsed`. 22 | type Timer: Default; 23 | 24 | /// Creates a new timer that expires after the given time in ms. 25 | fn make_timer(&mut self, milliseconds: usize) -> Self::Timer; 26 | 27 | /// Checks whether a given timer is expired. 28 | /// 29 | /// Until a timer expires, this function consistently returns false. Once it expires, this 30 | /// function consistently returns true. In particular, it is valid to continue calling this 31 | /// function after the first time it returns true. 32 | fn is_elapsed(&mut self, timer: &Self::Timer) -> bool; 33 | 34 | /// Timestamp in microseconds. 35 | /// 36 | /// Normal operation only needs relative time, absolute timestamps are useful for debugging. 37 | #[cfg(feature = "debug_ctap")] 38 | fn timestamp_us(&mut self) -> usize; 39 | } 40 | -------------------------------------------------------------------------------- /libraries/opensk/src/api/connection.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022-2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::ctap::status_code::{Ctap2StatusCode, CtapResult}; 16 | use core::convert::TryFrom; 17 | 18 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 19 | pub enum UsbEndpoint { 20 | MainHid = 1, 21 | #[cfg(feature = "vendor_hid")] 22 | VendorHid = 2, 23 | } 24 | 25 | impl TryFrom for UsbEndpoint { 26 | type Error = Ctap2StatusCode; 27 | 28 | fn try_from(endpoint_num: usize) -> CtapResult { 29 | match endpoint_num { 30 | 1 => Ok(UsbEndpoint::MainHid), 31 | #[cfg(feature = "vendor_hid")] 32 | 2 => Ok(UsbEndpoint::VendorHid), 33 | _ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_HARDWARE_FAILURE), 34 | } 35 | } 36 | } 37 | 38 | pub enum RecvStatus { 39 | Timeout, 40 | Received(UsbEndpoint), 41 | } 42 | 43 | pub trait HidConnection { 44 | fn send(&mut self, buf: &[u8; 64], endpoint: UsbEndpoint) -> CtapResult<()>; 45 | fn recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> CtapResult; 46 | } 47 | 48 | #[cfg(test)] 49 | mod test { 50 | use super::*; 51 | 52 | #[test] 53 | fn test_endpoint_num() { 54 | assert_eq!(UsbEndpoint::try_from(1), Ok(UsbEndpoint::MainHid)); 55 | #[cfg(feature = "vendor_hid")] 56 | assert_eq!(UsbEndpoint::try_from(2), Ok(UsbEndpoint::VendorHid)); 57 | assert_eq!( 58 | UsbEndpoint::try_from(3), 59 | Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_HARDWARE_FAILURE) 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /libraries/opensk/src/api/crypto/aes256.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::{AES_BLOCK_SIZE, AES_KEY_SIZE}; 16 | 17 | /// Encrypts and decrypts data using AES256. 18 | pub trait Aes256 { 19 | /// Creates a new key from its bytes. 20 | fn new(key: &[u8; AES_KEY_SIZE]) -> Self; 21 | 22 | /// Encrypts a block in place. 23 | fn encrypt_block(&self, block: &mut [u8; AES_BLOCK_SIZE]); 24 | 25 | /// Decrypts a block in place. 26 | fn decrypt_block(&self, block: &mut [u8; AES_BLOCK_SIZE]); 27 | 28 | /// Encrypts a message in place using CBC mode. 29 | /// 30 | /// # Panics 31 | /// 32 | /// Panics if the plaintext is not a multiple of the block size. 33 | fn encrypt_cbc(&self, iv: &[u8; AES_BLOCK_SIZE], plaintext: &mut [u8]); 34 | 35 | /// Decrypts a message in place using CBC mode. 36 | /// 37 | /// # Panics 38 | /// 39 | /// Panics if the ciphertext is not a multiple of the block size. 40 | fn decrypt_cbc(&self, iv: &[u8; AES_BLOCK_SIZE], ciphertext: &mut [u8]); 41 | } 42 | -------------------------------------------------------------------------------- /libraries/opensk/src/api/crypto/ecdh.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::EC_FIELD_SIZE; 16 | use crate::api::rng::Rng; 17 | 18 | /// Container for all ECDH cryptographic material. 19 | pub trait Ecdh { 20 | type SecretKey: SecretKey; 21 | type PublicKey: PublicKey; 22 | type SharedSecret: SharedSecret; 23 | } 24 | 25 | /// ECDH ephemeral key. 26 | pub trait SecretKey { 27 | type PublicKey: PublicKey; 28 | type SharedSecret: SharedSecret; 29 | 30 | /// Generates a new random secret key. 31 | fn random(rng: &mut impl Rng) -> Self; 32 | 33 | /// Computes the corresponding public key for this private key. 34 | fn public_key(&self) -> Self::PublicKey; 35 | 36 | /// Computes the shared secret when using Elliptic-curve Diffie–Hellman. 37 | fn diffie_hellman(&self, public_key: &Self::PublicKey) -> Self::SharedSecret; 38 | } 39 | 40 | /// ECDH public key. 41 | pub trait PublicKey: Sized { 42 | /// Creates a public key from its coordinates. 43 | fn from_coordinates(x: &[u8; EC_FIELD_SIZE], y: &[u8; EC_FIELD_SIZE]) -> Option; 44 | 45 | /// Writes the public key coordinates into the passed in parameters. 46 | fn to_coordinates(&self, x: &mut [u8; EC_FIELD_SIZE], y: &mut [u8; EC_FIELD_SIZE]); 47 | } 48 | 49 | /// ECDH shared secret. 50 | pub trait SharedSecret { 51 | /// Exports the x component of the point computed by Diffie–Hellman. 52 | fn raw_secret_bytes(&self, secret: &mut [u8; EC_FIELD_SIZE]); 53 | } 54 | -------------------------------------------------------------------------------- /libraries/opensk/src/api/crypto/ecdsa.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::{EC_FIELD_SIZE, EC_SIGNATURE_SIZE, HASH_SIZE}; 16 | use crate::api::rng::Rng; 17 | use alloc::vec::Vec; 18 | 19 | /// Container for all ECDSA cryptographic material. 20 | pub trait Ecdsa { 21 | type SecretKey: SecretKey; 22 | type PublicKey: PublicKey; 23 | type Signature: Signature; 24 | } 25 | 26 | /// ECDSA signing key. 27 | pub trait SecretKey: Sized { 28 | type PublicKey: PublicKey; 29 | type Signature: Signature; 30 | 31 | /// Generates a new random secret key. 32 | fn random(rng: &mut impl Rng) -> Self; 33 | 34 | /// Creates a signing key from its representation in bytes. 35 | fn from_slice(bytes: &[u8; EC_FIELD_SIZE]) -> Option; 36 | 37 | /// Computes the corresponding public key for this private key. 38 | fn public_key(&self) -> Self::PublicKey; 39 | 40 | /// Signs the message. 41 | /// 42 | /// For hashing, SHA256 is used implicitly. 43 | fn sign(&self, message: &[u8]) -> Self::Signature; 44 | 45 | /// Writes the signing key bytes into the passed in parameter. 46 | fn to_slice(&self, bytes: &mut [u8; EC_FIELD_SIZE]); 47 | } 48 | 49 | /// ECDSA verifying key. 50 | pub trait PublicKey: Sized { 51 | type Signature: Signature; 52 | 53 | /// Creates a public key from its coordinates. 54 | fn from_coordinates(x: &[u8; EC_FIELD_SIZE], y: &[u8; EC_FIELD_SIZE]) -> Option; 55 | 56 | /// Verifies if the signature matches the message. 57 | /// 58 | /// For hashing, SHA256 is used implicitly. 59 | fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool; 60 | 61 | /// Verifies if the signature matches the hash of the message. 62 | /// 63 | /// Prehash is the SHA256 of the signed message. 64 | fn verify_prehash(&self, prehash: &[u8; HASH_SIZE], signature: &Self::Signature) -> bool; 65 | 66 | /// Writes the public key coordinates into the passed in parameters. 67 | fn to_coordinates(&self, x: &mut [u8; EC_FIELD_SIZE], y: &mut [u8; EC_FIELD_SIZE]); 68 | } 69 | 70 | /// ECDSA signature. 71 | pub trait Signature: Sized { 72 | /// Creates a signature from its affine coordinates, represented as concatenated bytes. 73 | fn from_slice(bytes: &[u8; EC_SIGNATURE_SIZE]) -> Option; 74 | 75 | /// Writes the signature bytes into the passed in parameter. 76 | fn to_slice(&self, bytes: &mut [u8; EC_SIGNATURE_SIZE]); 77 | 78 | /// Encodes the signatures as ASN1 DER. 79 | fn to_der(&self) -> Vec; 80 | } 81 | -------------------------------------------------------------------------------- /libraries/opensk/src/api/crypto/hkdf256.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::HASH_SIZE; 16 | 17 | /// HKDF using SHA256. 18 | pub trait Hkdf256 { 19 | /// Computes the HKDF with 256 bit (one block) output. 20 | /// 21 | /// # Arguments 22 | /// 23 | /// * `ikm` - Input keying material 24 | /// * `salt` - Byte string that acts as a key 25 | /// * `info` - Optional context and application specific information 26 | /// 27 | /// This implementation is equivalent to a standard HKD, with `salt` fixed at a length of 28 | /// 32 byte and the output length l as 32. 29 | fn hkdf_256(ikm: &[u8], salt: &[u8; HASH_SIZE], info: &[u8], okm: &mut [u8; HASH_SIZE]); 30 | 31 | /// Computes the HKDF with empty salt and 256 bit (one block) output. 32 | /// 33 | /// # Arguments 34 | /// 35 | /// * `ikm` - Input keying material 36 | /// * `info` - Optional context and application specific information 37 | /// 38 | /// This implementation is equivalent to a standard HKDF, with `salt` set to the 39 | /// default block of zeros and the output length l as 32. 40 | fn hkdf_empty_salt_256(ikm: &[u8], info: &[u8], okm: &mut [u8; HASH_SIZE]) { 41 | Self::hkdf_256(ikm, &[0; HASH_SIZE], info, okm) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /libraries/opensk/src/api/crypto/hmac256.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::{HASH_SIZE, HMAC_KEY_SIZE, TRUNCATED_HMAC_SIZE}; 16 | 17 | /// For a given hash function, computes and verifies the HMAC. 18 | pub trait Hmac256 { 19 | /// Computes the HMAC. 20 | fn mac(key: &[u8; HMAC_KEY_SIZE], data: &[u8], output: &mut [u8; HASH_SIZE]); 21 | 22 | /// Verifies the HMAC. 23 | /// 24 | /// This function does best effort to not leak information about the key through side-channels 25 | /// (e.g. usage of constant time comparison). 26 | fn verify(key: &[u8; HMAC_KEY_SIZE], data: &[u8], mac: &[u8; HASH_SIZE]) -> bool; 27 | 28 | /// Verifies the first bytes of an HMAC. 29 | /// 30 | /// This function does best effort to not leak information about the key through side-channels 31 | /// (e.g. usage of constant time comparison). 32 | fn verify_truncated_left( 33 | key: &[u8; HMAC_KEY_SIZE], 34 | data: &[u8], 35 | mac: &[u8; TRUNCATED_HMAC_SIZE], 36 | ) -> bool; 37 | } 38 | -------------------------------------------------------------------------------- /libraries/opensk/src/api/crypto/sha256.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use super::HASH_SIZE; 16 | 17 | /// Hashes data using SHA256. 18 | /// 19 | /// When you implement this trait, make sure to implement `digest_mut` first, because the default 20 | /// implementations of `digest` relies on it. 21 | pub trait Sha256: Sized { 22 | /// Computes the hash of a given message as an array. 23 | /// 24 | /// This function does not let you control the memory allocation. It should therefore not be 25 | /// used for secrets that need zeroization. 26 | fn digest(data: &[u8]) -> [u8; HASH_SIZE] { 27 | let mut output = [0; HASH_SIZE]; 28 | Self::digest_mut(data, &mut output); 29 | output 30 | } 31 | 32 | /// Computes the hash of a given message directly. 33 | fn digest_mut(data: &[u8], output: &mut [u8; HASH_SIZE]) { 34 | let mut hasher = Self::new(); 35 | hasher.update(data); 36 | hasher.finalize(output) 37 | } 38 | 39 | /// Create a new object that can be incrementally updated for digesting. 40 | fn new() -> Self; 41 | 42 | /// Digest the next part of the message to hash. 43 | fn update(&mut self, data: &[u8]); 44 | 45 | /// Finalizes the hashing process, returns the hash value. 46 | fn finalize(self, output: &mut [u8; HASH_SIZE]); 47 | } 48 | -------------------------------------------------------------------------------- /libraries/opensk/src/api/firmware_protection.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022-2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | pub trait FirmwareProtection { 16 | /// Locks the firmware. 17 | /// 18 | /// Returns whether the operation was successful. 19 | fn lock(&mut self) -> bool; 20 | } 21 | -------------------------------------------------------------------------------- /libraries/opensk/src/api/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022-2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! APIs for the environment. 16 | //! 17 | //! The [environment](crate::env::Env) is split into components. Each component has an API described 18 | //! by a trait. This module gathers the API of those components. 19 | 20 | pub mod clock; 21 | pub mod connection; 22 | pub mod crypto; 23 | pub mod customization; 24 | pub mod firmware_protection; 25 | pub mod key_store; 26 | pub mod persist; 27 | pub mod private_key; 28 | pub mod rng; 29 | pub mod user_presence; 30 | -------------------------------------------------------------------------------- /libraries/opensk/src/api/rng.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | pub use rand_core; 16 | use rand_core::{CryptoRng, RngCore}; 17 | 18 | /// Random number generator. 19 | /// 20 | /// Reuses the common API from `RngCore`. Implementing the marker trait `CryptoRng` asserts that 21 | /// your random output is usable for sensitive key material. 22 | pub trait Rng: CryptoRng + RngCore { 23 | /// Provides a random byte array. 24 | /// 25 | /// This is a convenience function, as CTAP often requires such keys or tokens. 26 | fn gen_uniform_u8x32(&mut self) -> [u8; 32] { 27 | let mut bytes = [0; 32]; 28 | self.fill_bytes(&mut bytes); 29 | bytes 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /libraries/opensk/src/api/user_presence.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022-2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::api::connection::RecvStatus; 16 | use crate::ctap::status_code::CtapResult; 17 | 18 | #[derive(Debug)] 19 | pub enum UserPresenceError { 20 | /// User explicitly declined user presence check. 21 | Declined, 22 | /// User presence check was canceled by User Agent. 23 | Canceled, 24 | /// User presence check timed out. 25 | Timeout, 26 | } 27 | pub type UserPresenceResult = Result<(), UserPresenceError>; 28 | 29 | pub type UserPresenceWaitResult = CtapResult<(UserPresenceResult, RecvStatus)>; 30 | 31 | pub trait UserPresence { 32 | /// Initializes for a user presence check. 33 | /// 34 | /// Must eventually be followed by a call to [`Self::check_complete`]. 35 | fn check_init(&mut self); 36 | 37 | /// Waits until user presence is confirmed, rejected, or the given timeout expires. 38 | /// 39 | /// Must be called between calls to [`Self::check_init`] and [`Self::check_complete`]. 40 | /// The function may write to the packet buffer, if it receives one during the wait. 41 | /// If it does, the returned option contains the endpoint it received the data from. 42 | fn wait_with_timeout( 43 | &mut self, 44 | packet: &mut [u8; 64], 45 | timeout_ms: usize, 46 | ) -> UserPresenceWaitResult; 47 | 48 | /// Finalizes a user presence check. 49 | /// 50 | /// Must be called after [`Self::check_init`]. 51 | fn check_complete(&mut self); 52 | } 53 | -------------------------------------------------------------------------------- /libraries/opensk/src/ctap/secret.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022-2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Provides secret handling support. 16 | //! 17 | //! This module provides the `Secret` type to store secrets of type `T` while minimizing the 18 | //! amount of time the secret is present in RAM. This type ensures that: 19 | //! - The secret only lives in one place. It is not implicitly copied (or equivalently moved). 20 | //! - The secret is zeroed out (using the `zeroize` crate) after usage. 21 | //! 22 | //! It is possible to escape those principles: 23 | //! - By using functions containing the sequence of words "expose secret" in their name. 24 | //! - By explicitly cloning or copying the secret. 25 | //! 26 | //! We don't use the secrecy crate because the SecretBox is incorrect and it doesn't provide a 27 | //! mutable version of ExposeSecret. 28 | //! 29 | //! Also note that eventually, we may use some Randomize trait instead of Zeroize, to prevent 30 | //! side-channels. 31 | 32 | use alloc::boxed::Box; 33 | use alloc::vec; 34 | use alloc::vec::Vec; 35 | use core::ops::{Deref, DerefMut}; 36 | use zeroize::Zeroize; 37 | 38 | /// Defines a secret that is zeroized on Drop. 39 | #[derive(Debug, Clone, PartialEq, Eq)] 40 | pub struct Secret(Box); 41 | 42 | impl Secret { 43 | /// Imports an already exposed secret. 44 | /// 45 | /// This is provided for convenience and should be avoided when possible. The expected usage is 46 | /// to create a default secret, then write its content in place. 47 | pub fn from_exposed_secret(secret: T) -> Self { 48 | Self(Box::new(secret)) 49 | } 50 | } 51 | 52 | impl Secret<[u8]> { 53 | pub fn new(len: usize) -> Self { 54 | Self(vec![0; len].into_boxed_slice()) 55 | } 56 | 57 | /// Extracts the secret as a Vec. 58 | /// 59 | /// This means that the secret won't be zeroed-out on Drop. 60 | pub fn expose_secret_to_vec(mut self) -> Vec { 61 | core::mem::take(&mut self.0).into() 62 | } 63 | } 64 | 65 | impl Default for Secret { 66 | fn default() -> Self { 67 | Secret(Box::default()) 68 | } 69 | } 70 | 71 | impl Drop for Secret { 72 | fn drop(&mut self) { 73 | self.0.zeroize(); 74 | } 75 | } 76 | 77 | impl Deref for Secret { 78 | type Target = T; 79 | 80 | fn deref(&self) -> &Self::Target { 81 | self.0.deref() 82 | } 83 | } 84 | 85 | impl DerefMut for Secret { 86 | fn deref_mut(&mut self) -> &mut Self::Target { 87 | self.0.deref_mut() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /libraries/opensk/src/env/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022-2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::api::clock::Clock; 16 | use crate::api::connection::HidConnection; 17 | use crate::api::crypto::ecdh::Ecdh; 18 | use crate::api::crypto::ecdsa::Ecdsa; 19 | use crate::api::crypto::Crypto; 20 | use crate::api::customization::Customization; 21 | use crate::api::key_store::KeyStore; 22 | use crate::api::persist::Persist; 23 | use crate::api::rng::Rng; 24 | use crate::api::user_presence::UserPresence; 25 | use crate::ctap::Channel; 26 | use alloc::vec::Vec; 27 | 28 | #[cfg(feature = "std")] 29 | pub mod test; 30 | 31 | pub type AesKey = <::Crypto as Crypto>::Aes256; 32 | pub type EcdhSk = <<::Crypto as Crypto>::Ecdh as Ecdh>::SecretKey; 33 | pub type EcdhPk = <<::Crypto as Crypto>::Ecdh as Ecdh>::PublicKey; 34 | pub type EcdsaSk = <<::Crypto as Crypto>::Ecdsa as Ecdsa>::SecretKey; 35 | pub type EcdsaPk = <<::Crypto as Crypto>::Ecdsa as Ecdsa>::PublicKey; 36 | pub type EcdsaSignature = <<::Crypto as Crypto>::Ecdsa as Ecdsa>::Signature; 37 | pub type Sha = <::Crypto as Crypto>::Sha256; 38 | pub type Hmac = <::Crypto as Crypto>::Hmac256; 39 | pub type Hkdf = <::Crypto as Crypto>::Hkdf256; 40 | 41 | /// Describes what CTAP needs to function. 42 | pub trait Env { 43 | type Rng: Rng; 44 | type UserPresence: UserPresence; 45 | type Persist: Persist; 46 | type KeyStore: KeyStore; 47 | type Write: core::fmt::Write; 48 | type Customization: Customization; 49 | type HidConnection: HidConnection; 50 | type Clock: Clock; 51 | type Crypto: Crypto; 52 | 53 | fn rng(&mut self) -> &mut Self::Rng; 54 | fn user_presence(&mut self) -> &mut Self::UserPresence; 55 | fn persist(&mut self) -> &mut Self::Persist; 56 | fn key_store(&mut self) -> &mut Self::KeyStore; 57 | fn clock(&mut self) -> &mut Self::Clock; 58 | 59 | /// Creates a write instance for debugging. 60 | /// 61 | /// This API doesn't return a reference such that drop may flush. This matches the Tock 62 | /// environment. Non-Tock embedded environments should use the defmt feature (to be implemented 63 | /// using the defmt crate) and ignore this API. Non-embedded environments may either use this 64 | /// API or use the log feature (to be implemented using the log crate). 65 | fn write(&mut self) -> Self::Write; 66 | 67 | fn customization(&self) -> &Self::Customization; 68 | 69 | /// I/O connection for sending HID packets. 70 | fn hid_connection(&mut self) -> &mut Self::HidConnection; 71 | 72 | /// Indicates that the last power cycle was not caused by user action. 73 | fn boots_after_soft_reset(&self) -> bool; 74 | 75 | /// Option to return a firmware version that is shown as device info. 76 | fn firmware_version(&self) -> Option { 77 | None 78 | } 79 | 80 | /// Option to process a CBOR command before standard parsing. 81 | /// 82 | /// Responses are sent on the same channel they were received. Return `None` to continue 83 | /// regular parsing. Parsing vendor commands may still have side effects, like dropping state 84 | /// from other commands. 85 | /// 86 | /// For standard commands, the format for bytes is one byte of CTAP2 command and a CBOR encoded 87 | /// map. To be able to reliably detect your payload, consider starting your messages with a 88 | /// vendor reserved command byte. 89 | fn process_vendor_command(&mut self, _bytes: &[u8], _channel: Channel) -> Option> { 90 | None 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /libraries/opensk/src/test_helpers/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022-2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::api::persist::{Attestation, AttestationId, Persist}; 16 | use crate::ctap::command::{AuthenticatorConfigParameters, Command}; 17 | use crate::ctap::data_formats::ConfigSubCommand; 18 | use crate::ctap::secret::Secret; 19 | use crate::ctap::status_code::CtapResult; 20 | use crate::ctap::{Channel, CtapState}; 21 | use crate::env::Env; 22 | 23 | // In tests where we define a dummy user-presence check that immediately returns, the channel 24 | // ID is irrelevant, so we pass this (dummy but valid) value. 25 | const DUMMY_CHANNEL: Channel = Channel::MainHid([0x12, 0x34, 0x56, 0x78]); 26 | 27 | pub fn enable_enterprise_attestation( 28 | state: &mut CtapState, 29 | env: &mut E, 30 | ) -> CtapResult { 31 | let attestation = Attestation { 32 | private_key: Secret::from_exposed_secret([0x41; 32]), 33 | certificate: vec![0xdd; 20], 34 | }; 35 | env.persist() 36 | .set_attestation(AttestationId::Enterprise, Some(&attestation))?; 37 | 38 | let config_params = AuthenticatorConfigParameters { 39 | sub_command: ConfigSubCommand::EnableEnterpriseAttestation, 40 | sub_command_params: None, 41 | pin_uv_auth_param: None, 42 | pin_uv_auth_protocol: None, 43 | }; 44 | let config_command = Command::AuthenticatorConfig(config_params); 45 | state.process_parsed_command(env, config_command, DUMMY_CHANNEL)?; 46 | 47 | Ok(attestation) 48 | } 49 | -------------------------------------------------------------------------------- /libraries/persistent_store/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "persistent_store" 3 | version = "0.1.0" 4 | authors = ["Julien Cretin "] 5 | license = "Apache-2.0" 6 | edition = "2018" 7 | 8 | [dependencies] 9 | 10 | [dev-dependencies] 11 | tempfile = "3" 12 | 13 | [features] 14 | std = [] 15 | -------------------------------------------------------------------------------- /libraries/persistent_store/fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | /artifacts/ 2 | /corpus/ 3 | /target/ 4 | -------------------------------------------------------------------------------- /libraries/persistent_store/fuzz/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "arbitrary" 7 | version = "0.4.7" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "db55d72333851e17d572bec876e390cd3b11eb1ef53ae821dd9f3b653d2b4569" 10 | 11 | [[package]] 12 | name = "cc" 13 | version = "1.2.25" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" 16 | dependencies = [ 17 | "shlex", 18 | ] 19 | 20 | [[package]] 21 | name = "fuzz-store" 22 | version = "0.0.0" 23 | dependencies = [ 24 | "libfuzzer-sys", 25 | "persistent_store", 26 | "rand_core", 27 | "rand_pcg", 28 | "strum", 29 | ] 30 | 31 | [[package]] 32 | name = "heck" 33 | version = "0.3.3" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 36 | dependencies = [ 37 | "unicode-segmentation", 38 | ] 39 | 40 | [[package]] 41 | name = "libfuzzer-sys" 42 | version = "0.3.5" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "fcf184a4b6b274f82a5df6b357da6055d3e82272327bba281c28bbba6f1664ef" 45 | dependencies = [ 46 | "arbitrary", 47 | "cc", 48 | ] 49 | 50 | [[package]] 51 | name = "persistent_store" 52 | version = "0.1.0" 53 | 54 | [[package]] 55 | name = "proc-macro2" 56 | version = "1.0.95" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 59 | dependencies = [ 60 | "unicode-ident", 61 | ] 62 | 63 | [[package]] 64 | name = "quote" 65 | version = "1.0.40" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 68 | dependencies = [ 69 | "proc-macro2", 70 | ] 71 | 72 | [[package]] 73 | name = "rand_core" 74 | version = "0.5.1" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 77 | 78 | [[package]] 79 | name = "rand_pcg" 80 | version = "0.2.1" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" 83 | dependencies = [ 84 | "rand_core", 85 | ] 86 | 87 | [[package]] 88 | name = "shlex" 89 | version = "1.3.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 92 | 93 | [[package]] 94 | name = "strum" 95 | version = "0.19.5" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "b89a286a7e3b5720b9a477b23253bc50debac207c8d21505f8e70b36792f11b5" 98 | dependencies = [ 99 | "strum_macros", 100 | ] 101 | 102 | [[package]] 103 | name = "strum_macros" 104 | version = "0.19.4" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "e61bb0be289045cb80bfce000512e32d09f8337e54c186725da381377ad1f8d5" 107 | dependencies = [ 108 | "heck", 109 | "proc-macro2", 110 | "quote", 111 | "syn", 112 | ] 113 | 114 | [[package]] 115 | name = "syn" 116 | version = "1.0.109" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 119 | dependencies = [ 120 | "proc-macro2", 121 | "quote", 122 | "unicode-ident", 123 | ] 124 | 125 | [[package]] 126 | name = "unicode-ident" 127 | version = "1.0.18" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 130 | 131 | [[package]] 132 | name = "unicode-segmentation" 133 | version = "1.12.0" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 136 | -------------------------------------------------------------------------------- /libraries/persistent_store/fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fuzz-store" 3 | version = "0.0.0" 4 | authors = ["Julien Cretin "] 5 | publish = false 6 | edition = "2018" 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [dependencies] 12 | libfuzzer-sys = "0.3" 13 | persistent_store = { path = "..", features = ["std"] } 14 | rand_core = "0.5" 15 | rand_pcg = "0.2" 16 | strum = { version = "0.19", features = ["derive"] } 17 | 18 | # Prevent this from interfering with workspaces 19 | [workspace] 20 | members = ["."] 21 | 22 | [[bin]] 23 | name = "store" 24 | path = "fuzz_targets/store.rs" 25 | -------------------------------------------------------------------------------- /libraries/persistent_store/fuzz/fuzz_targets/store.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![no_main] 16 | 17 | use libfuzzer_sys::fuzz_target; 18 | 19 | fuzz_target!(|data: &[u8]| { 20 | fuzz_store::fuzz(data, false, None); 21 | }); 22 | -------------------------------------------------------------------------------- /libraries/persistent_store/fuzz/src/histogram.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::num_bits; 16 | use std::collections::HashMap; 17 | 18 | /// Histogram with logarithmic buckets. 19 | /// 20 | /// This is used to compute coverage statistics of the fuzzing runs of a corpus. This is not used 21 | /// during actual fuzzing, only when replaying a corpus to compute statistics. 22 | #[derive(Default)] 23 | pub struct Histogram { 24 | /// Maps each bucket to its count. 25 | /// 26 | /// Buckets are numbers sharing the same highest bit. The first buckets are: only 0, only 1, 2 27 | /// to 3, 4 to 7, 8 to 15. Buckets are identified by their lower-bound. 28 | buckets: HashMap, 29 | } 30 | 31 | impl Histogram { 32 | /// Increases the count of the bucket of an item. 33 | /// 34 | /// The bucket of `item` is the highest power of two, lower or equal to `item`. If `item` is 35 | /// zero, then its bucket is also zero. 36 | pub fn add(&mut self, item: usize) { 37 | *self.buckets.entry(get_bucket(item)).or_insert(0) += 1; 38 | } 39 | 40 | /// Merges another histogram into this one. 41 | pub fn merge(&mut self, other: &Histogram) { 42 | for (&bucket, &count) in &other.buckets { 43 | *self.buckets.entry(bucket).or_insert(0) += count; 44 | } 45 | } 46 | 47 | /// Returns the bit-width of one past the highest non-empty bucket. 48 | /// 49 | /// In other words, all non-empty buckets of the histogram have a bit-width smaller than the 50 | /// returned width. 51 | pub fn width_lim(&self) -> usize { 52 | self.buckets.keys().max().map_or(0, |&x| num_bits(x) + 1) 53 | } 54 | 55 | /// Returns the count of a bucket. 56 | pub fn get(&self, bucket: usize) -> Option { 57 | self.buckets.get(&bucket).cloned() 58 | } 59 | 60 | /// Returns the total count. 61 | pub fn count(&self) -> usize { 62 | self.buckets.values().sum() 63 | } 64 | } 65 | 66 | /// Returns the bucket of an item. 67 | fn get_bucket(item: usize) -> usize { 68 | let bucket = bucket_from_width(num_bits(item)); 69 | assert!(bucket <= item && (item == 0 || item / 2 < bucket)); 70 | bucket 71 | } 72 | 73 | /// Returns the bucket of an item given its bit-width. 74 | pub fn bucket_from_width(width: usize) -> usize { 75 | if width == 0 { 76 | 0 77 | } else { 78 | 1 << (width - 1) 79 | } 80 | } 81 | 82 | #[test] 83 | fn get_bucket_ok() { 84 | assert_eq!(get_bucket(0), 0); 85 | assert_eq!(get_bucket(1), 1); 86 | assert_eq!(get_bucket(2), 2); 87 | assert_eq!(get_bucket(3), 2); 88 | assert_eq!(get_bucket(4), 4); 89 | assert_eq!(get_bucket(7), 4); 90 | assert_eq!(get_bucket(8), 8); 91 | assert_eq!(get_bucket(15), 8); 92 | } 93 | -------------------------------------------------------------------------------- /libraries/persistent_store/src/test.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::{BufferOptions, BufferStorage, Store, StoreDriverOff}; 16 | 17 | #[derive(Clone)] 18 | pub struct Config { 19 | pub word_size: usize, 20 | pub page_size: usize, 21 | pub num_pages: usize, 22 | pub max_word_writes: usize, 23 | pub max_page_erases: usize, 24 | } 25 | 26 | impl Config { 27 | pub fn new_driver(&self) -> StoreDriverOff { 28 | StoreDriverOff::new(self.into(), self.num_pages) 29 | } 30 | 31 | pub fn new_store(&self) -> Store { 32 | self.new_driver().power_on().unwrap().extract_store() 33 | } 34 | } 35 | 36 | impl<'a> From<&'a Config> for BufferOptions { 37 | fn from(config: &'a Config) -> Self { 38 | BufferOptions { 39 | word_size: config.word_size, 40 | page_size: config.page_size, 41 | max_word_writes: config.max_word_writes, 42 | max_page_erases: config.max_page_erases, 43 | strict_mode: true, 44 | } 45 | } 46 | } 47 | 48 | pub const MINIMAL: Config = Config { 49 | word_size: 4, 50 | page_size: 64, 51 | num_pages: 5, 52 | max_word_writes: 2, 53 | max_page_erases: 9, 54 | }; 55 | 56 | const NORDIC: Config = Config { 57 | word_size: 4, 58 | page_size: 0x1000, 59 | num_pages: 20, 60 | max_word_writes: 2, 61 | max_page_erases: 10000, 62 | }; 63 | 64 | const TITAN: Config = Config { 65 | word_size: 4, 66 | page_size: 0x800, 67 | num_pages: 10, 68 | max_word_writes: 2, 69 | max_page_erases: 10000, 70 | }; 71 | 72 | #[test] 73 | fn nordic_capacity() { 74 | let driver = NORDIC.new_driver().power_on().unwrap(); 75 | assert_eq!(driver.model().capacity().total, 19123); 76 | } 77 | 78 | #[test] 79 | fn titan_capacity() { 80 | let driver = TITAN.new_driver().power_on().unwrap(); 81 | assert_eq!(driver.model().capacity().total, 4315); 82 | } 83 | 84 | #[test] 85 | fn minimal_virt_page_size() { 86 | // Make sure a virtual page has 14 words. We use this property in the other tests below to 87 | // know whether entries are spanning, starting, and ending pages. 88 | assert_eq!(MINIMAL.new_driver().model().format().virt_page_size(), 14); 89 | } 90 | -------------------------------------------------------------------------------- /maintainers/reproduce_board.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2019 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -ex 17 | 18 | echo "Board: $BOARD" 19 | echo "========================================" >> reproducible/elf2tab.txt 20 | echo "Board: $BOARD" >> reproducible/elf2tab.txt 21 | echo "----------------------------------------" >> reproducible/elf2tab.txt 22 | 23 | ./deploy.py --verbose-build --disable-check-patches --board=$BOARD --no-app --programmer=none 24 | ./third_party/tock/tools/sha256sum/target/debug/sha256sum third_party/tock/target/thumbv7em-none-eabi/release/$BOARD.bin >> reproducible/binaries.sha256sum 25 | tar -rvf reproducible/reproduced.tar third_party/tock/target/thumbv7em-none-eabi/release/$BOARD.bin 26 | 27 | ./deploy.py --verbose-build --disable-check-patches --board=$BOARD --opensk --programmer=none --elf2tab-output=reproducible/elf2tab.txt 28 | ./third_party/tock/tools/sha256sum/target/debug/sha256sum target/${BOARD}_merged.hex >> reproducible/binaries.sha256sum 29 | tar -rvf reproducible/reproduced.tar target/${BOARD}_merged.hex 30 | -------------------------------------------------------------------------------- /maintainers/reproduce_hashes.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2019 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -ex 17 | 18 | rm -f reproducible/binaries.sha256sum 19 | rm -f reproducible/elf2tab.txt 20 | 21 | echo "Creating reproducible/reproduced.tar" 22 | touch empty_file 23 | tar -cvf reproducible/reproduced.tar empty_file 24 | rm empty_file 25 | 26 | echo "Building sha256sum tool..." 27 | cargo build --manifest-path third_party/tock/tools/sha256sum/Cargo.toml 28 | 29 | echo "Computing SHA-256 sums of the boards..." 30 | for board in nrf52840dk_opensk nrf52840_dongle_opensk nrf52840_dongle_dfu nrf52840_mdk_dfu 31 | do 32 | BOARD=$board ./maintainers/reproduce_board.sh 33 | done 34 | 35 | echo "Computing SHA-256 sum of the TAB file..." 36 | ./third_party/tock/tools/sha256sum/target/debug/sha256sum target/tab/ctap2.tab >> reproducible/binaries.sha256sum 37 | tar -rvf reproducible/reproduced.tar target/tab/ctap2.tab 38 | 39 | tar -rvf reproducible/reproduced.tar reproducible/elf2tab.txt 40 | tar -rvf reproducible/reproduced.tar reproducible/binaries.sha256sum 41 | -------------------------------------------------------------------------------- /maintainers/update_hashes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eux 4 | 5 | mkdir -p tmp 6 | 7 | for OS in macos-10.15 ubuntu-18.04 8 | do 9 | unzip reproduced-$OS.zip -d tmp/reproduced-$OS/ 10 | tar -C tmp/reproduced-$OS/ -xvf tmp/reproduced-$OS/reproduced.tar 11 | cp tmp/reproduced-$OS/reproducible/binaries.sha256sum reproducible/reference_binaries_$OS.sha256sum 12 | cp tmp/reproduced-$OS/reproducible/elf2tab.txt reproducible/reference_elf2tab_$OS.txt 13 | done 14 | 15 | rm -R tmp 16 | -------------------------------------------------------------------------------- /nrf52840_layout.ld: -------------------------------------------------------------------------------- 1 | /* Layout for the nRF52840-DK and nRF52840 dongle, used by the 2 | * app in this repository. 3 | */ 4 | 5 | MEMORY { 6 | /* The application region is 64 bytes (0x40) and we reserve 0x40000 at the end 7 | * of the flash for the persistent storage. 8 | */ 9 | FLASH (X) : ORIGIN = 0x00040040, LENGTH = 0x0007FFC0 10 | RAM (W) : ORIGIN = 0x20020000, LENGTH = 128K 11 | } 12 | 13 | TBF_HEADER_SIZE = 0x60; 14 | INCLUDE libtock_layout.ld 15 | -------------------------------------------------------------------------------- /nrf52840_layout_a.ld: -------------------------------------------------------------------------------- 1 | /* Layout for the nRF52840-DK and nRF52840 dongle, used by the 2 | * app in this repository. 3 | */ 4 | 5 | MEMORY { 6 | /* The application region is 64 bytes (0x40) and we reserve 0x40000 at the end 7 | * of the flash for the persistent storage. 8 | */ 9 | FLASH (X) : ORIGIN = 0x00040040, LENGTH = 0x0001FFC0 10 | RAM (W) : ORIGIN = 0x20020000, LENGTH = 128K 11 | } 12 | 13 | TBF_HEADER_SIZE = 0x60; 14 | INCLUDE libtock_layout.ld 15 | -------------------------------------------------------------------------------- /nrf52840_layout_b.ld: -------------------------------------------------------------------------------- 1 | /* Layout for the nRF52840-DK and nRF52840 dongle, used by the 2 | * app in this repository. 3 | */ 4 | 5 | MEMORY { 6 | /* The application region is 64 bytes (0x40) and we reserve 0x40000 at the end 7 | * of the flash for the persistent storage. 8 | */ 9 | FLASH (X) : ORIGIN = 0x00080040, LENGTH = 0x0001FFC0 10 | RAM (W) : ORIGIN = 0x20020000, LENGTH = 128K 11 | } 12 | 13 | TBF_HEADER_SIZE = 0x60; 14 | INCLUDE libtock_layout.ld 15 | -------------------------------------------------------------------------------- /patches/tock/01-nrf52-opensk-boards.patch: -------------------------------------------------------------------------------- 1 | diff --git a/Cargo.toml b/Cargo.toml 2 | index 04cb39334..596cacda4 100644 3 | --- a/Cargo.toml 4 | +++ b/Cargo.toml 5 | @@ -23,7 +23,13 @@ members = [ 6 | "boards/msp_exp432p401r", 7 | "boards/microbit_v2", 8 | "boards/nordic/nrf52840dk", 9 | + "boards/nordic/nrf52840dk_opensk", 10 | + "boards/nordic/nrf52840dk_opensk_a", 11 | + "boards/nordic/nrf52840dk_opensk_b", 12 | "boards/nordic/nrf52840_dongle", 13 | + "boards/nordic/nrf52840_dongle_opensk", 14 | + "boards/nordic/nrf52840_dongle_dfu", 15 | + "boards/nordic/nrf52840_mdk_dfu", 16 | "boards/nordic/nrf52dk", 17 | "boards/nucleo_f429zi", 18 | "boards/nucleo_f446re", 19 | -------------------------------------------------------------------------------- /patches/tock/05-kernel-utility-method.patch: -------------------------------------------------------------------------------- 1 | diff --git a/kernel/src/syscall_driver.rs b/kernel/src/syscall_driver.rs 2 | index 0e2943fe4..3b5b42486 100644 3 | --- a/kernel/src/syscall_driver.rs 4 | +++ b/kernel/src/syscall_driver.rs 5 | @@ -102,6 +102,19 @@ impl CommandReturn { 6 | self.0 7 | } 8 | 9 | + /// Check whether the inner `SyscallReturn` value is successful 10 | + pub fn is_success(&self) -> bool { 11 | + matches!( 12 | + self.0, 13 | + SyscallReturn::Success 14 | + | SyscallReturn::SuccessU32(_) 15 | + | SyscallReturn::SuccessU32U32(_, _) 16 | + | SyscallReturn::SuccessU32U32U32(_, _, _) 17 | + | SyscallReturn::SuccessU32U64(_, _) 18 | + | SyscallReturn::SuccessU64(_) 19 | + ) 20 | + } 21 | + 22 | /// Command error 23 | pub fn failure(rc: ErrorCode) -> Self { 24 | CommandReturn(SyscallReturn::Failure(rc)) 25 | -------------------------------------------------------------------------------- /reproducible/reference_binaries_macos-10.15.sha256sum: -------------------------------------------------------------------------------- 1 | dd5920dfb172d9371b29d019b6a37fae1a995bf9d814000944d9ef36bad31513 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin 2 | 18f111cd3f86b9e06979f2f16465d217bde6af4613e561883c32235992b57099 target/nrf52840dk_merged.hex 3 | e4acfa602a5cc5d7c61d465f873918e8e0858628d0e5f8e0db26a7b7dd0b94d4 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin 4 | dd283a1949a9ffb7bf81ef5a0dcd4c45adfb8dda8344a672bff160a917e0b6b9 target/nrf52840_dongle_merged.hex 5 | c0ace9f13ef3fd18c576a735ae23b3956bf8dd346f20c6217086e748d6bad8a2 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin 6 | 11c0dad7abd513066732952fd5ad9988b0b45339683d93fcd8f4660d62d93aa1 target/nrf52840_dongle_dfu_merged.hex 7 | 06a38a0d6d356145467a73c765e28a945878f663664016f888393207097bfe10 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin 8 | c584d6e22b0a4a80fccc1e557ed95c744a02f12107f7a5b3c5ec31f06a0f781f target/nrf52840_mdk_dfu_merged.hex 9 | c56962d46ad423b61bb0edc39d8a4a135c22e59fee440ddbfcade3f8136e7b39 target/tab/ctap2.tab 10 | -------------------------------------------------------------------------------- /reproducible/reference_binaries_ubuntu-18.04.sha256sum: -------------------------------------------------------------------------------- 1 | 2426ee9a6c75e325537818081d45445d95468a4c0a77feacdc6133d7d9aa227a third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin 2 | a5c6deb3931715c003ad79ccd2847bf5085b20d888908f5b589531077f55752f target/nrf52840dk_merged.hex 3 | c53d1e1db72df25950fa6d28699a2d38757def0dcbeb0d09d2366481cf0149a6 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin 4 | eff4f034398895536391d52e3874c15b7952dd113e455994b09847ed9fb04160 target/nrf52840_dongle_merged.hex 5 | 233b5ba4459523759e3171cee83cdb3a383bbe65727c8ece64dfe5321d6ebe34 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin 6 | 29c664a35a3e400a1608573e56313bf1364e648174467e4a64de78c434a5caf0 target/nrf52840_dongle_dfu_merged.hex 7 | 1baaf518a74c6077cb936d9cf178b6dd0232e7562fa56174886b05b77886cc32 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin 8 | 0d175e760518c1734b425e291f0d60c39b4f5e8c96dbffca5f17f4fc225551f0 target/nrf52840_mdk_dfu_merged.hex 9 | cfad3b9f3d6ee1a80f4e47a66af49875c19c37c363699780529f946c6c9c29b9 target/tab/ctap2.tab 10 | -------------------------------------------------------------------------------- /reproducible/reference_elf2tab_macos-10.15.txt: -------------------------------------------------------------------------------- 1 | ======================================== 2 | Board: nrf52840dk 3 | ---------------------------------------- 4 | Creating "target/tab/thumbv7em-none-eabi.tbf" 5 | Min RAM size from segments in ELF: 20 bytes 6 | Number of writeable flash regions: 0 7 | Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. 8 | Entry point is in .text section 9 | Adding .text section. Offset: 128 (0x80). Length: 187288 (0x2db98) bytes. 10 | Adding .stack section. Offset: 187416 (0x2dc18). Length: 16384 (0x4000) bytes. 11 | Searching for .rel.X sections to add. 12 | TBF Header: 13 | version: 2 0x2 14 | header_size: 56 0x38 15 | total_size: 262144 0x40000 16 | flags: 1 0x1 17 | 18 | init_fn_offset: 73 0x49 19 | protected_size: 8 0x8 20 | minimum_ram_size: 107428 0x1A3A4 21 | 22 | start_process_ram: 4294967295 0xFFFFFFFF 23 | start_process_flash: 262208 0x40040 24 | 25 | ======================================== 26 | Board: nrf52840_dongle 27 | ---------------------------------------- 28 | Creating "target/tab/thumbv7em-none-eabi.tbf" 29 | Min RAM size from segments in ELF: 20 bytes 30 | Number of writeable flash regions: 0 31 | Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. 32 | Entry point is in .text section 33 | Adding .text section. Offset: 128 (0x80). Length: 187288 (0x2db98) bytes. 34 | Adding .stack section. Offset: 187416 (0x2dc18). Length: 16384 (0x4000) bytes. 35 | Searching for .rel.X sections to add. 36 | TBF Header: 37 | version: 2 0x2 38 | header_size: 56 0x38 39 | total_size: 262144 0x40000 40 | flags: 1 0x1 41 | 42 | init_fn_offset: 73 0x49 43 | protected_size: 8 0x8 44 | minimum_ram_size: 107428 0x1A3A4 45 | 46 | start_process_ram: 4294967295 0xFFFFFFFF 47 | start_process_flash: 262208 0x40040 48 | 49 | ======================================== 50 | Board: nrf52840_dongle_dfu 51 | ---------------------------------------- 52 | Creating "target/tab/thumbv7em-none-eabi.tbf" 53 | Min RAM size from segments in ELF: 20 bytes 54 | Number of writeable flash regions: 0 55 | Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. 56 | Entry point is in .text section 57 | Adding .text section. Offset: 128 (0x80). Length: 187288 (0x2db98) bytes. 58 | Adding .stack section. Offset: 187416 (0x2dc18). Length: 16384 (0x4000) bytes. 59 | Searching for .rel.X sections to add. 60 | TBF Header: 61 | version: 2 0x2 62 | header_size: 56 0x38 63 | total_size: 262144 0x40000 64 | flags: 1 0x1 65 | 66 | init_fn_offset: 73 0x49 67 | protected_size: 8 0x8 68 | minimum_ram_size: 107428 0x1A3A4 69 | 70 | start_process_ram: 4294967295 0xFFFFFFFF 71 | start_process_flash: 262208 0x40040 72 | 73 | ======================================== 74 | Board: nrf52840_mdk_dfu 75 | ---------------------------------------- 76 | Creating "target/tab/thumbv7em-none-eabi.tbf" 77 | Min RAM size from segments in ELF: 20 bytes 78 | Number of writeable flash regions: 0 79 | Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. 80 | Entry point is in .text section 81 | Adding .text section. Offset: 128 (0x80). Length: 187288 (0x2db98) bytes. 82 | Adding .stack section. Offset: 187416 (0x2dc18). Length: 16384 (0x4000) bytes. 83 | Searching for .rel.X sections to add. 84 | TBF Header: 85 | version: 2 0x2 86 | header_size: 56 0x38 87 | total_size: 262144 0x40000 88 | flags: 1 0x1 89 | 90 | init_fn_offset: 73 0x49 91 | protected_size: 8 0x8 92 | minimum_ram_size: 107428 0x1A3A4 93 | 94 | start_process_ram: 4294967295 0xFFFFFFFF 95 | start_process_flash: 262208 0x40040 96 | 97 | -------------------------------------------------------------------------------- /reproducible/reference_elf2tab_ubuntu-18.04.txt: -------------------------------------------------------------------------------- 1 | ======================================== 2 | Board: nrf52840dk 3 | ---------------------------------------- 4 | Creating "target/tab/thumbv7em-none-eabi.tbf" 5 | Min RAM size from segments in ELF: 20 bytes 6 | Number of writeable flash regions: 0 7 | Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. 8 | Entry point is in .text section 9 | Adding .text section. Offset: 128 (0x80). Length: 186992 (0x2da70) bytes. 10 | Adding .stack section. Offset: 187120 (0x2daf0). Length: 16384 (0x4000) bytes. 11 | Searching for .rel.X sections to add. 12 | TBF Header: 13 | version: 2 0x2 14 | header_size: 56 0x38 15 | total_size: 262144 0x40000 16 | flags: 1 0x1 17 | 18 | init_fn_offset: 73 0x49 19 | protected_size: 8 0x8 20 | minimum_ram_size: 107428 0x1A3A4 21 | 22 | start_process_ram: 4294967295 0xFFFFFFFF 23 | start_process_flash: 262208 0x40040 24 | 25 | ======================================== 26 | Board: nrf52840_dongle 27 | ---------------------------------------- 28 | Creating "target/tab/thumbv7em-none-eabi.tbf" 29 | Min RAM size from segments in ELF: 20 bytes 30 | Number of writeable flash regions: 0 31 | Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. 32 | Entry point is in .text section 33 | Adding .text section. Offset: 128 (0x80). Length: 186992 (0x2da70) bytes. 34 | Adding .stack section. Offset: 187120 (0x2daf0). Length: 16384 (0x4000) bytes. 35 | Searching for .rel.X sections to add. 36 | TBF Header: 37 | version: 2 0x2 38 | header_size: 56 0x38 39 | total_size: 262144 0x40000 40 | flags: 1 0x1 41 | 42 | init_fn_offset: 73 0x49 43 | protected_size: 8 0x8 44 | minimum_ram_size: 107428 0x1A3A4 45 | 46 | start_process_ram: 4294967295 0xFFFFFFFF 47 | start_process_flash: 262208 0x40040 48 | 49 | ======================================== 50 | Board: nrf52840_dongle_dfu 51 | ---------------------------------------- 52 | Creating "target/tab/thumbv7em-none-eabi.tbf" 53 | Min RAM size from segments in ELF: 20 bytes 54 | Number of writeable flash regions: 0 55 | Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. 56 | Entry point is in .text section 57 | Adding .text section. Offset: 128 (0x80). Length: 186992 (0x2da70) bytes. 58 | Adding .stack section. Offset: 187120 (0x2daf0). Length: 16384 (0x4000) bytes. 59 | Searching for .rel.X sections to add. 60 | TBF Header: 61 | version: 2 0x2 62 | header_size: 56 0x38 63 | total_size: 262144 0x40000 64 | flags: 1 0x1 65 | 66 | init_fn_offset: 73 0x49 67 | protected_size: 8 0x8 68 | minimum_ram_size: 107428 0x1A3A4 69 | 70 | start_process_ram: 4294967295 0xFFFFFFFF 71 | start_process_flash: 262208 0x40040 72 | 73 | ======================================== 74 | Board: nrf52840_mdk_dfu 75 | ---------------------------------------- 76 | Creating "target/tab/thumbv7em-none-eabi.tbf" 77 | Min RAM size from segments in ELF: 20 bytes 78 | Number of writeable flash regions: 0 79 | Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. 80 | Entry point is in .text section 81 | Adding .text section. Offset: 128 (0x80). Length: 186992 (0x2da70) bytes. 82 | Adding .stack section. Offset: 187120 (0x2daf0). Length: 16384 (0x4000) bytes. 83 | Searching for .rel.X sections to add. 84 | TBF Header: 85 | version: 2 0x2 86 | header_size: 56 0x38 87 | total_size: 262144 0x40000 88 | flags: 1 0x1 89 | 90 | init_fn_offset: 73 0x49 91 | protected_size: 8 0x8 92 | minimum_ram_size: 107428 0x1A3A4 93 | 94 | start_process_ram: 4294967295 0xFFFFFFFF 95 | start_process_flash: 262208 0x40040 96 | 97 | -------------------------------------------------------------------------------- /reproducible/sample_crypto_data/aaguid.txt: -------------------------------------------------------------------------------- 1 | 664d9f67-84a2-412a-9ff7-b4f7d8ee6d05 2 | -------------------------------------------------------------------------------- /reproducible/sample_crypto_data/opensk.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BggqhkjOPQMBBw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MHcCAQEEICMfnFy7L3y5p2MOGezavAeS+noKYtT21mDcWllN7Y1zoAoGCCqGSM49 6 | AwEHoUQDQgAEhZflF2Fq4xmAofKOxG/0sx8bucdpJPRLR4HXArAFXJzdLF9ofkpn 7 | gzsVWzTYFr+nNiyxySyJsdkH/qQv4rCV0A== 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /reproducible/sample_crypto_data/opensk_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBPDCB4wIUTEbgPPL3tr2rLkI83EyzyQJQmYEwCgYIKoZIzj0EAwIwGzEZMBcG 3 | A1UEAwwQR29vZ2xlIE9wZW5TSyBDQTAeFw0yMDA0MTQxNTM5MDRaFw0zMDA0MTQx 4 | NTM5MDRaMCcxJTAjBgNVBAMMHEdvb2dsZSBPcGVuU0sgSGFja2VyIEVkaXRpb24w 5 | WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASFl+UXYWrjGYCh8o7Eb/SzHxu5x2kk 6 | 9EtHgdcCsAVcnN0sX2h+SmeDOxVbNNgWv6c2LLHJLImx2Qf+pC/isJXQMAoGCCqG 7 | SM49BAMCA0gAMEUCIBKkHijpTbjlPDv3oFw/nW/ta8jEMhY8iNCBp9N0+NNYAiEA 8 | ywzrGpmc0reEUFCGHBBdvC2E2SxIlvaefz7umT8ajy4= 9 | -----END CERTIFICATE----- 10 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # General setup 2 | tockloader == 1.5 3 | six 4 | intelhex 5 | 6 | # Configure OpenSK (crypto, JTAG lockdown) 7 | colorama 8 | tqdm 9 | cryptography 10 | fido2 >= 1.0.0 11 | 12 | # Tests 13 | pylint 14 | yapf 15 | hid 16 | -------------------------------------------------------------------------------- /reset.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2019 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | echo "$(tput bold)This script will restore the repository to a clean state$(tput sgr0)" 17 | echo "$(tput bold)Any pending change will be lost.$(tput sgr0)" 18 | echo "" 19 | 20 | accept='' 21 | while 22 | case "$accept" in 23 | [Yy]) 24 | # Start echoeing the commands to the screen 25 | set -x 26 | # Reset the submodules 27 | git submodule foreach 'git reset --hard && git clean -fxd' 28 | # Reset also the main repository 29 | git reset --hard && git clean -fxd --exclude elf2tab 30 | 31 | set +x 32 | echo "DONE." 33 | # And break the loop 34 | false 35 | ;; 36 | 37 | [Nn]) 38 | echo "Nothing was done. Repository was left untouched." 39 | # Don't do anything but break the while loop to exit 40 | false 41 | ;; 42 | 43 | *) 44 | # Continue looping 45 | true 46 | ;; 47 | esac 48 | do 49 | echo "$(tput bold)Are you sure you want to continue? [y/n]$(tput sgr0)" 50 | read -s -n 1 accept 51 | done 52 | -------------------------------------------------------------------------------- /rules.d/55-opensk.rules: -------------------------------------------------------------------------------- 1 | SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="1915", ATTRS{idProduct}=="521f", ATTRS{product}=="OpenSK", MODE="0660", GROUP="logindev", TAG+="uaccess" 2 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2024-05-01" 3 | components = ["clippy", "miri", "rustfmt", "rust-src"] 4 | targets = ["thumbv7em-none-eabi", 5 | "riscv32imac-unknown-none-elf", 6 | "riscv32imc-unknown-none-elf"] 7 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_field_init_shorthand = true 2 | use_try_shorthand = true 3 | edition = "2018" 4 | imports_granularity = "Module" 5 | -------------------------------------------------------------------------------- /setup-submodules.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2020 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # Ensure the script doesn't fail on Github workflows 17 | export TERM=${TERM:-vt100} 18 | done_text="$(tput bold)DONE.$(tput sgr0)" 19 | 20 | set -e 21 | shopt -s nullglob 22 | 23 | # Ensure the submodules are pulled and up-to-date 24 | git submodule update --init 25 | 26 | patch_conflict_detected () { 27 | cat </dev/null 31 | then 32 | echo "Missing $1 command.$2" 33 | exit 1 34 | fi 35 | } 36 | check_command rustup " Follow the steps under https://rustup.rs/ to install it." 37 | python3 -m venv "$PY_VENV_NAME" 38 | "$PYTHON" -m pip install --upgrade pip setuptools wheel 39 | check_command "$PIP" 40 | 41 | # Ensure we have certificates, keys, etc. so that the tests can run 42 | source tools/gen_key_materials.sh 43 | generate_pki N 44 | if [ ! -f "crypto_data/opensk.key" -o ! -f "crypto_data/opensk_cert.pem" ] 45 | then 46 | generate_new_batch 47 | fi 48 | 49 | rustup show 50 | "$PIP" install --upgrade -r requirements.txt 51 | 52 | # Install dependency to create applications. 53 | mkdir -p elf2tab 54 | rustup install stable 55 | cargo +stable install cargo-audit 56 | cargo +stable install elf2tab --version 0.10.2 --root elf2tab/ 57 | -------------------------------------------------------------------------------- /src/env/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | pub mod tock; 16 | -------------------------------------------------------------------------------- /src/env/tock/phantom_buffer_storage.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use alloc::borrow::Cow; 16 | use core::marker::PhantomData; 17 | use libtock_platform as platform; 18 | use libtock_platform::Syscalls; 19 | use persistent_store::{ 20 | BufferCorruptFunction, BufferOptions, BufferStorage, Storage, StorageIndex, StorageResult, 21 | }; 22 | 23 | /// Wrapper with phantom data for the test storage implementation. 24 | pub struct PhantomBufferStorage< 25 | S: Syscalls, 26 | C: platform::subscribe::Config + platform::allow_ro::Config, 27 | > { 28 | storage: BufferStorage, 29 | s: PhantomData, 30 | c: PhantomData, 31 | } 32 | 33 | impl PhantomBufferStorage 34 | where 35 | S: Syscalls, 36 | C: platform::allow_ro::Config + platform::subscribe::Config, 37 | { 38 | pub fn new(storage: Box<[u8]>, options: BufferOptions) -> Self { 39 | Self { 40 | storage: BufferStorage::new(storage, options), 41 | s: PhantomData, 42 | c: PhantomData, 43 | } 44 | } 45 | 46 | pub fn arm_interruption(&mut self, delay: usize) { 47 | self.storage.arm_interruption(delay); 48 | } 49 | 50 | pub fn disarm_interruption(&mut self) -> usize { 51 | self.storage.disarm_interruption() 52 | } 53 | 54 | pub fn reset_interruption(&mut self) { 55 | self.storage.reset_interruption(); 56 | } 57 | 58 | pub fn corrupt_operation(&mut self, corrupt: BufferCorruptFunction) { 59 | self.storage.corrupt_operation(corrupt); 60 | } 61 | 62 | pub fn get_word_writes(&self, word: usize) -> usize { 63 | self.storage.get_word_writes(word) 64 | } 65 | 66 | pub fn get_page_erases(&self, page: usize) -> usize { 67 | self.storage.get_page_erases(page) 68 | } 69 | 70 | pub fn set_page_erases(&mut self, page: usize, cycle: usize) { 71 | self.storage.set_page_erases(page, cycle); 72 | } 73 | } 74 | 75 | impl Storage for PhantomBufferStorage 76 | where 77 | S: Syscalls, 78 | C: platform::allow_ro::Config + platform::subscribe::Config, 79 | { 80 | fn word_size(&self) -> usize { 81 | self.storage.word_size() 82 | } 83 | 84 | fn page_size(&self) -> usize { 85 | self.storage.page_size() 86 | } 87 | 88 | fn num_pages(&self) -> usize { 89 | self.storage.num_pages() 90 | } 91 | 92 | fn max_word_writes(&self) -> usize { 93 | self.storage.max_word_writes() 94 | } 95 | 96 | fn max_page_erases(&self) -> usize { 97 | self.storage.max_page_erases() 98 | } 99 | 100 | fn read_slice(&self, index: StorageIndex, length: usize) -> StorageResult> { 101 | self.storage.read_slice(index, length) 102 | } 103 | 104 | fn write_slice(&mut self, index: StorageIndex, value: &[u8]) -> StorageResult<()> { 105 | self.storage.write_slice(index, value) 106 | } 107 | 108 | fn erase_page(&mut self, page: usize) -> StorageResult<()> { 109 | self.storage.erase_page(page) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![cfg_attr(not(feature = "std"), no_std)] 16 | 17 | extern crate alloc; 18 | 19 | pub mod env; 20 | -------------------------------------------------------------------------------- /third_party/lang-items/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lang_items" 3 | version = "0.1.0" 4 | authors = [ 5 | "Tock Project Developers ", 6 | "Guillaume Endignoux ", 7 | ] 8 | license = "MIT/Apache-2.0" 9 | edition = "2018" 10 | 11 | [target.'cfg(any(target_arch = "arm", target_arch = "riscv32"))'.dependencies.libtock_runtime] 12 | path = "../../third_party/libtock-rs/runtime" 13 | default-features = false 14 | features = ["no_auto_layout", "alloc_init"] 15 | 16 | [dependencies] 17 | libtock_drivers = { path = "../libtock-drivers" } 18 | libtock_platform = { path = "../../third_party/libtock-rs/platform" } 19 | libtock_low_level_debug = { path = "../../third_party/libtock-rs/apis/low_level_debug" } 20 | libtock_leds = { path = "../../third_party/libtock-rs/apis/leds" } 21 | libtock_alarm = { path = "../../third_party/libtock-rs/apis/alarm" } 22 | libtock_console = { path = "../../third_party/libtock-rs/apis/console" } 23 | 24 | [dependencies.linked_list_allocator] 25 | version = "0.10.4" 26 | default-features = false 27 | features = ["const_mut_refs"] 28 | 29 | [features] 30 | debug_allocations = [] 31 | panic_console = [] 32 | std = [] 33 | -------------------------------------------------------------------------------- /third_party/lang-items/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 The Tock Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /third_party/lang-items/src/allocator.rs: -------------------------------------------------------------------------------- 1 | use crate::util; 2 | use core::alloc::{GlobalAlloc, Layout}; 3 | #[cfg(any(feature = "debug_allocations", feature = "panic_console"))] 4 | use core::fmt::Write; 5 | use core::ptr; 6 | use core::ptr::NonNull; 7 | #[cfg(feature = "debug_allocations")] 8 | use core::sync::atomic; 9 | #[cfg(feature = "debug_allocations")] 10 | use core::sync::atomic::AtomicUsize; 11 | #[cfg(any(feature = "debug_allocations", feature = "panic_console"))] 12 | use libtock_console::Console; 13 | #[cfg(feature = "panic_console")] 14 | use libtock_platform::{ErrorCode, Syscalls}; 15 | use libtock_runtime::TockSyscalls; 16 | use linked_list_allocator::Heap; 17 | 18 | static mut HEAP: Heap = Heap::empty(); 19 | 20 | #[no_mangle] 21 | unsafe fn libtock_alloc_init(app_heap_bottom: *mut u8, app_heap_size: usize) { 22 | HEAP.init(app_heap_bottom, app_heap_size); 23 | } 24 | 25 | // With the "debug_allocations" feature, we use `AtomicUsize` to store the 26 | // statistics because: 27 | // - it is `Sync`, so we can use it in a static object (the allocator), 28 | // - it implements interior mutability, so we can use it in the allocator 29 | // methods (that take an immutable `&self` reference). 30 | struct TockAllocator { 31 | #[cfg(feature = "debug_allocations")] 32 | count: AtomicUsize, 33 | #[cfg(feature = "debug_allocations")] 34 | size: AtomicUsize, 35 | } 36 | 37 | impl TockAllocator { 38 | const fn new() -> TockAllocator { 39 | TockAllocator { 40 | #[cfg(feature = "debug_allocations")] 41 | count: AtomicUsize::new(0), 42 | #[cfg(feature = "debug_allocations")] 43 | size: AtomicUsize::new(0), 44 | } 45 | } 46 | } 47 | 48 | unsafe impl GlobalAlloc for TockAllocator { 49 | #[allow(clippy::let_and_return)] 50 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 51 | let ptr = HEAP 52 | .allocate_first_fit(layout) 53 | .ok() 54 | .map_or(ptr::null_mut(), NonNull::as_ptr); 55 | #[cfg(feature = "debug_allocations")] 56 | { 57 | self.count.fetch_add(1, atomic::Ordering::SeqCst); 58 | self.size.fetch_add(layout.size(), atomic::Ordering::SeqCst); 59 | writeln!( 60 | Console::::writer(), 61 | "alloc[{}, {}] = {:?} ({} ptrs, {} bytes)", 62 | layout.size(), 63 | layout.align(), 64 | ptr, 65 | self.count.load(atomic::Ordering::SeqCst), 66 | self.size.load(atomic::Ordering::SeqCst) 67 | ) 68 | .unwrap(); 69 | } 70 | ptr 71 | } 72 | 73 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 74 | #[cfg(feature = "debug_allocations")] 75 | { 76 | self.count.fetch_sub(1, atomic::Ordering::SeqCst); 77 | self.size.fetch_sub(layout.size(), atomic::Ordering::SeqCst); 78 | writeln!( 79 | Console::::writer(), 80 | "dealloc[{}, {}] = {:?} ({} ptrs, {} bytes)", 81 | layout.size(), 82 | layout.align(), 83 | ptr, 84 | self.count.load(atomic::Ordering::SeqCst), 85 | self.size.load(atomic::Ordering::SeqCst) 86 | ) 87 | .unwrap(); 88 | } 89 | HEAP.deallocate(NonNull::new_unchecked(ptr), layout) 90 | } 91 | } 92 | 93 | #[cfg(any(target_arch = "arm", target_arch = "riscv32"))] 94 | #[global_allocator] 95 | static ALLOCATOR: TockAllocator = TockAllocator::new(); 96 | 97 | #[alloc_error_handler] 98 | unsafe fn alloc_error_handler(_layout: Layout) -> ! { 99 | util::Util::::signal_oom(); 100 | util::Util::::signal_panic(); 101 | 102 | #[cfg(feature = "panic_console")] 103 | { 104 | writeln!( 105 | Console::::writer(), 106 | "Couldn't allocate: {:?}", 107 | _layout 108 | ) 109 | .ok(); 110 | TockSyscalls::exit_terminate(ErrorCode::Fail as u32); 111 | } 112 | 113 | #[cfg(not(feature = "panic_console"))] 114 | util::Util::::cycle_leds() 115 | } 116 | -------------------------------------------------------------------------------- /third_party/lang-items/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | #![feature(alloc_error_handler)] 3 | 4 | #[cfg(not(feature = "std"))] 5 | mod allocator; 6 | #[cfg(not(feature = "std"))] 7 | mod panic_handler; 8 | #[cfg(not(feature = "std"))] 9 | mod util; 10 | 11 | #[cfg(feature = "std")] 12 | #[no_mangle] 13 | unsafe fn libtock_alloc_init(_app_heap_bottom: *mut u8, _app_heap_size: usize) { 14 | // Stub so that the symbol is present. 15 | unimplemented!() 16 | } 17 | -------------------------------------------------------------------------------- /third_party/lang-items/src/panic_handler.rs: -------------------------------------------------------------------------------- 1 | //! Custom panic handler for OpenSK 2 | 3 | use crate::util; 4 | #[cfg(feature = "panic_console")] 5 | use core::fmt::Write; 6 | #[cfg(feature = "panic_console")] 7 | use libtock_console::Console; 8 | #[allow(unused_imports)] 9 | use libtock_platform::{ErrorCode, Syscalls}; 10 | use libtock_runtime::TockSyscalls; 11 | 12 | #[panic_handler] 13 | fn panic_handler(_info: &core::panic::PanicInfo) -> ! { 14 | util::Util::::signal_panic(); 15 | 16 | #[cfg(feature = "panic_console")] 17 | { 18 | let mut writer = Console::::writer(); 19 | writeln!(writer, "{}", _info).ok(); 20 | // Exit with a non-zero exit code to indicate failure. 21 | TockSyscalls::exit_terminate(ErrorCode::Fail as u32); 22 | } 23 | #[cfg(not(feature = "panic_console"))] 24 | util::Util::::flash_all_leds(); 25 | } 26 | -------------------------------------------------------------------------------- /third_party/lang-items/src/util.rs: -------------------------------------------------------------------------------- 1 | use libtock_alarm::{Alarm, Milliseconds}; 2 | use libtock_leds::Leds; 3 | use libtock_low_level_debug::{AlertCode, LowLevelDebug}; 4 | use libtock_platform as platform; 5 | use libtock_platform::Syscalls; 6 | use platform::DefaultConfig; 7 | 8 | pub struct Util(S, C); 9 | 10 | impl Util { 11 | /// Signal a panic using the LowLevelDebug capsule (if available). 12 | pub fn signal_panic() { 13 | LowLevelDebug::::print_alert_code(AlertCode::Panic); 14 | } 15 | 16 | /// Signal an out-of-memory error using the LowLevelDebug capsule (if available). 17 | pub fn signal_oom() { 18 | LowLevelDebug::::print_alert_code(AlertCode::WrongLocation); 19 | } 20 | 21 | #[allow(dead_code)] 22 | pub fn flash_all_leds() -> ! { 23 | // Flash all LEDs (if available). All errors from syscalls are ignored: we are already inside a 24 | // panic handler so there is nothing much to do if simple drivers (timer, LEDs) don't work. 25 | loop { 26 | if let Ok(led_count) = Leds::::count() { 27 | for led in 0..led_count { 28 | let _ = Leds::::on(led); 29 | } 30 | } 31 | let _ = Alarm::::sleep_for(Milliseconds(100)); 32 | if let Ok(led_count) = Leds::::count() { 33 | for led in 0..led_count { 34 | let _ = Leds::::off(led); 35 | } 36 | } 37 | let _ = Alarm::::sleep_for(Milliseconds(100)); 38 | } 39 | } 40 | 41 | #[allow(dead_code)] 42 | pub fn cycle_leds() -> ! { 43 | // Cycle though all LEDs (if available). All errors from syscalls are ignored: we are already 44 | // inside an error handler so there is nothing much to do if simple drivers (timer, LEDs) don't 45 | // work. 46 | loop { 47 | if let Ok(leds) = Leds::::count() { 48 | for led in 0..leds { 49 | let _ = Leds::::on(led); 50 | let _ = Alarm::::sleep_for(Milliseconds(100)); 51 | let _ = Leds::::off(led); 52 | } 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /third_party/libtock-drivers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_drivers" 3 | version = "0.1.0" 4 | authors = [ 5 | "Tock Project Developers ", 6 | "Guillaume Endignoux ", 7 | ] 8 | license = "MIT/Apache-2.0" 9 | edition = "2018" 10 | 11 | [dependencies] 12 | libtock_alarm = { path = "../../third_party/libtock-rs/apis/alarm" } 13 | libtock_buttons = { path = "../../third_party/libtock-rs/apis/buttons" } 14 | libtock_console = { path = "../../third_party/libtock-rs/apis/console" } 15 | libtock_platform = { path = "../../third_party/libtock-rs/platform" } 16 | 17 | [features] 18 | debug_ctap = [] 19 | verbose_usb = ["debug_ctap"] 20 | with_nfc = [] 21 | -------------------------------------------------------------------------------- /third_party/libtock-drivers/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 The Tock Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /third_party/libtock-drivers/src/crp.rs: -------------------------------------------------------------------------------- 1 | use crate::result::TockResult; 2 | use libtock_platform as platform; 3 | use libtock_platform::{DefaultConfig, Syscalls}; 4 | use platform::ErrorCode; 5 | 6 | const DRIVER_NUMBER: u32 = 0x00008; 7 | 8 | mod command_nr { 9 | pub const AVAILABLE: u32 = 0; 10 | pub const GET_PROTECTION: u32 = 1; 11 | pub const SET_PROTECTION: u32 = 2; 12 | } 13 | 14 | #[derive(PartialOrd, PartialEq, Eq)] 15 | pub enum ProtectionLevel { 16 | /// Unsupported feature 17 | Unknown = 0, 18 | /// This should be the factory default for the chip. 19 | NoProtection = 1, 20 | /// At this level, only JTAG/SWD are disabled but other debugging 21 | /// features may still be enabled. 22 | JtagDisabled = 2, 23 | /// This is the maximum level of protection the chip supports. 24 | /// At this level, JTAG and all other features are expected to be 25 | /// disabled and only a full chip erase may allow to recover from 26 | /// that state. 27 | FullyLocked = 0xff, 28 | } 29 | 30 | pub struct Crp(S, C); 31 | 32 | impl From for ProtectionLevel { 33 | fn from(value: u32) -> Self { 34 | match value { 35 | 1 => ProtectionLevel::NoProtection, 36 | 2 => ProtectionLevel::JtagDisabled, 37 | 0xff => ProtectionLevel::FullyLocked, 38 | _ => ProtectionLevel::Unknown, 39 | } 40 | } 41 | } 42 | 43 | impl Crp { 44 | pub fn is_available() -> TockResult<()> { 45 | S::command(DRIVER_NUMBER, command_nr::AVAILABLE, 0, 0).to_result::<(), ErrorCode>()?; 46 | 47 | Ok(()) 48 | } 49 | 50 | pub fn get_protection() -> TockResult { 51 | let protection_level = S::command(DRIVER_NUMBER, command_nr::GET_PROTECTION, 0, 0) 52 | .to_result::()?; 53 | 54 | Ok(protection_level.into()) 55 | } 56 | 57 | pub fn set_protection(level: ProtectionLevel) -> TockResult<()> { 58 | S::command(DRIVER_NUMBER, command_nr::SET_PROTECTION, level as u32, 0) 59 | .to_result::<(), ErrorCode>()?; 60 | 61 | Ok(()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /third_party/libtock-drivers/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | pub mod crp; 4 | #[cfg(feature = "with_nfc")] 5 | pub mod nfc; 6 | pub mod result; 7 | pub mod rng; 8 | pub mod storage; 9 | pub mod timer; 10 | pub mod usb_ctap_hid; 11 | pub mod util; 12 | -------------------------------------------------------------------------------- /third_party/libtock-drivers/src/result.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | use libtock_platform::ErrorCode; 4 | 5 | pub type TockResult = Result; 6 | 7 | // We sometimes need to handle errors in a `TockResult` by calling `unwrap`. However, 8 | // `Result::unwrap` requires that the error type implements `core::fmt::Debug`. Under the hood, 9 | // this requires dynamic dispatch, which has non-negligible overhead on code size. Therefore errors 10 | // don't derive from `Debug` in libtock-rs. 11 | // 12 | // Instead one can call `.ok().unwrap()` which relies on `Option::unwrap` and doesn't require any 13 | // debugging of the error type. 14 | // 15 | // This trait allows to flexibly use `Result::unwrap` or `Option::unwrap` and is configured to do 16 | // so depending on the `debug_ctap` feature. 17 | pub trait FlexUnwrap { 18 | #[track_caller] 19 | fn flex_unwrap(self) -> T; 20 | } 21 | 22 | impl FlexUnwrap for TockResult { 23 | #[cfg(feature = "debug_ctap")] 24 | fn flex_unwrap(self) -> T { 25 | self.unwrap() 26 | } 27 | 28 | #[cfg(not(feature = "debug_ctap"))] 29 | fn flex_unwrap(self) -> T { 30 | self.ok().unwrap() 31 | } 32 | } 33 | 34 | #[derive(Copy, Clone)] 35 | pub enum TockError { 36 | Command(ErrorCode), 37 | Format, 38 | Other(OtherError), 39 | } 40 | 41 | #[cfg(feature = "debug_ctap")] 42 | impl core::fmt::Debug for TockError { 43 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 44 | match self { 45 | TockError::Command(error_code) => { 46 | f.write_fmt(format_args!("CommandError: {:?}", error_code)) 47 | } 48 | TockError::Format => f.write_str("TockError::Format"), 49 | TockError::Other(e) => f.write_fmt(format_args!("OtherError: {:?}", e)), 50 | } 51 | } 52 | } 53 | 54 | impl From for TockError { 55 | fn from(command_error: ErrorCode) -> Self { 56 | TockError::Command(command_error) 57 | } 58 | } 59 | 60 | impl From for TockError { 61 | fn from(fmt::Error: fmt::Error) -> Self { 62 | TockError::Format 63 | } 64 | } 65 | 66 | #[derive(Copy, Clone)] 67 | #[cfg_attr(feature = "debug_ctap", derive(Debug))] 68 | pub enum OtherError { 69 | ButtonsDriverInvalidState, 70 | GpioDriverInvalidState, 71 | TimerDriverDurationOutOfRange, 72 | TimerDriverErroneousClockFrequency, 73 | DriversAlreadyTaken, 74 | OutOfRange, 75 | } 76 | 77 | impl From for TockError { 78 | fn from(other: OtherError) -> Self { 79 | TockError::Other(other) 80 | } 81 | } 82 | 83 | pub struct OutOfRangeError; 84 | 85 | impl From for TockError { 86 | fn from(_: OutOfRangeError) -> Self { 87 | TockError::Other(OtherError::OutOfRange) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /third_party/libtock-drivers/src/rng.rs: -------------------------------------------------------------------------------- 1 | //! Userspace interface for easy access to the random number generator 2 | 3 | use crate::util::Util; 4 | use core::cell::Cell; 5 | use core::convert::TryInto; 6 | use libtock_platform as platform; 7 | use libtock_platform::{share, AllowRw, DefaultConfig, Subscribe, Syscalls}; 8 | use platform::ErrorCode; 9 | 10 | /// Driver number for the random number generator 11 | const DRIVER_NUMBER: u32 = 0x40001; 12 | 13 | mod command_nr { 14 | pub const REQUEST_RNG: u32 = 1; 15 | } 16 | 17 | mod subscribe_nr { 18 | pub const BUFFER_FILLED: u32 = 0; 19 | } 20 | 21 | mod allow_nr { 22 | pub const SHARE_BUFFER: u32 = 0; 23 | } 24 | 25 | /// System call configuration trait for `Rng` 26 | pub trait Config: platform::allow_rw::Config + platform::subscribe::Config {} 27 | 28 | impl Config for T {} 29 | 30 | pub struct Rng(S, C); 31 | 32 | impl Rng { 33 | pub fn fill_buffer(buf: &mut [u8]) -> bool { 34 | let buf_len = buf.len(); 35 | let is_filled = Cell::new(false); 36 | 37 | share::scope::< 38 | ( 39 | AllowRw<_, DRIVER_NUMBER, { allow_nr::SHARE_BUFFER }>, 40 | Subscribe<_, DRIVER_NUMBER, { subscribe_nr::BUFFER_FILLED }>, 41 | ), 42 | _, 43 | _, 44 | >(|handle| { 45 | let (allow_rw, subscribe) = handle.split(); 46 | let result = S::allow_rw::(allow_rw, buf); 47 | if result.is_err() { 48 | return false; 49 | } 50 | 51 | // Automatically sets `is_filled` to true as soon as the buffer is filled. 52 | let subscription = 53 | S::subscribe::<_, _, C, DRIVER_NUMBER, { subscribe_nr::BUFFER_FILLED }>( 54 | subscribe, &is_filled, 55 | ); 56 | if subscription.is_err() { 57 | return false; 58 | } 59 | 60 | // Requests the random number generator to fill the buffer. 61 | let result_code: Result<(), ErrorCode> = S::command( 62 | DRIVER_NUMBER, 63 | command_nr::REQUEST_RNG, 64 | buf_len.try_into().unwrap(), 65 | 0, 66 | ) 67 | .to_result(); 68 | if result_code.is_err() { 69 | return false; 70 | } 71 | 72 | // Yields until the buffer is filled. 73 | Util::::yieldk_for(|| is_filled.get()); 74 | 75 | true 76 | }) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /third_party/libtock-drivers/src/util.rs: -------------------------------------------------------------------------------- 1 | use libtock_platform::Syscalls; 2 | 3 | pub struct Util(S); 4 | 5 | impl Util { 6 | // Yielding manually is discouraged as it conflicts with Rust's safety guarantees. 7 | // If you need to wait for a condition, use futures::wait_until and .await. 8 | pub fn yieldk_for bool>(cond: F) { 9 | while !cond() { 10 | S::yield_wait(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tools/authenticator_config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # Lint as: python3 15 | """Tool that sends a config command to OpenSK.""" 16 | 17 | from __future__ import absolute_import, division, print_function 18 | 19 | import argparse 20 | import colorama 21 | from fido2 import hid 22 | from fido2.ctap2 import Config 23 | import uuid 24 | 25 | from tools.configure import fatal, get_opensk_devices, info 26 | 27 | 28 | def main(args): 29 | colorama.init() 30 | 31 | devices = get_opensk_devices(False) 32 | if not devices: 33 | fatal("No devices found.") 34 | 35 | for authenticator in devices: 36 | if authenticator.device.capabilities & hid.CAPABILITY.WINK: 37 | authenticator.device.wink() 38 | aaguid = uuid.UUID(bytes=authenticator.get_info().aaguid) 39 | info(f"Config of device AAGUID {aaguid} ({authenticator.device}).") 40 | 41 | config = Config(authenticator) 42 | if args.ep: 43 | info("Enable EP...") 44 | config.enable_enterprise_attestation() 45 | if args.always_uv: 46 | info("Toggle AlwaysUv...") 47 | config.toggle_always_uv() 48 | 49 | 50 | if __name__ == "__main__": 51 | parser = argparse.ArgumentParser() 52 | parser.add_argument("--ep", action=argparse.BooleanOptionalAction) 53 | parser.add_argument("--always-uv", action=argparse.BooleanOptionalAction) 54 | main(parser.parse_args()) 55 | -------------------------------------------------------------------------------- /tools/openssl/opensk.conf: -------------------------------------------------------------------------------- 1 | oid_section = OIDS 2 | 3 | [ OIDS ] 4 | fido_attestation = 1.3.6.1.4.1.45724.2.1.1 5 | fido_aaguid = 1.3.6.1.4.1.45724.1.1.4 6 | 7 | [ req ] 8 | encrypt_key = no 9 | default_md = sha256 10 | utf8 = yes 11 | string_mask = utf8only 12 | prompt = no 13 | distinguished_name = fido_dn 14 | req_extensions = fido_reqext 15 | 16 | [ fido_dn ] 17 | countryName = "US" 18 | organizationName = "OpenSK" 19 | organizationalUnitName = "Authenticator Attestation" 20 | commonName = "OpenSK Hacker Edition" 21 | 22 | [ fido_reqext ] 23 | keyUsage = critical,digitalSignature 24 | subjectKeyIdentifier = hash 25 | fido_attestation = ASN1:FORMAT:BITLIST,BITSTRING:${ENV::OPENSK_TRANSPORT} 26 | fido_aaguid = ASN1:FORMAT:HEX,OCTETSTRING:${ENV::OPENSK_AAGUID} 27 | -------------------------------------------------------------------------------- /tools/openssl/root-ca.conf: -------------------------------------------------------------------------------- 1 | oid_section = OIDS 2 | 3 | [ default ] 4 | ca = root-ca 5 | dir = ./crypto_data 6 | 7 | [ req ] 8 | encrypt_key = yes 9 | default_md = sha256 10 | utf8 = yes 11 | string_mask = utf8only 12 | prompt = no 13 | distinguished_name = ca_dn 14 | req_extensions = ca_reqext 15 | 16 | [ OIDS ] 17 | fido_attestation = 1.3.6.1.4.1.45724.2.1.1 18 | fido_aaguid = 1.3.6.1.4.1.45724.1.1.4 19 | 20 | [ ca_dn ] 21 | countryName = "US" 22 | organizationName = "OpenSK" 23 | organizationalUnitName = "Authenticator Attestation" 24 | commonName = "OpenSK CA" 25 | 26 | [ ca_reqext ] 27 | keyUsage = critical,keyCertSign,cRLSign 28 | basicConstraints = critical,CA:true 29 | subjectKeyIdentifier = hash 30 | 31 | [ ca ] 32 | default_ca = root_ca 33 | 34 | [ root_ca ] 35 | certificate = $dir/ca/$ca.pem 36 | private_key = $dir/ca/$ca/private/$ca.key 37 | new_certs_dir = $dir/ca/$ca 38 | serial = $dir/ca/$ca/db/$ca.pem.srl 39 | crlnumber = $dir/ca/$ca/db/$ca.pem.srl 40 | database = $dir/ca/$ca/db/$ca.db 41 | unique_subject = no 42 | default_days = 36525 43 | default_md = sha256 44 | policy = match_pol 45 | email_in_dn = no 46 | preserve = no 47 | name_opt = ca_default 48 | cert_opt = ca_default 49 | copy_extensions = none 50 | x509_extensions = signing_ca_ext 51 | default_crl_days = 365 52 | crl_extensions = crl_ext 53 | 54 | [ match_pol ] 55 | countryName = match 56 | organizationName = match 57 | organizationalUnitName = match 58 | commonName = supplied 59 | 60 | [ any_pol ] 61 | countryName = optional 62 | stateOrProvinceName = optional 63 | localityName = optional 64 | organizationName = optional 65 | organizationalUnitName = optional 66 | commonName = optional 67 | emailAddress = optional 68 | 69 | [ root_ca_ext ] 70 | keyUsage = critical,keyCertSign,cRLSign 71 | basicConstraints = critical,CA:true 72 | subjectKeyIdentifier = hash 73 | authorityKeyIdentifier = keyid:always 74 | fido_attestation = ASN1:FORMAT:HEX,BITSTRING:00 75 | 76 | [ signing_ca_ext ] 77 | keyUsage = critical,keyCertSign,cRLSign 78 | basicConstraints = critical,CA:true,pathlen:0 79 | subjectKeyIdentifier = hash 80 | authorityKeyIdentifier = keyid:always 81 | 82 | [ crl_ext ] 83 | authorityKeyIdentifier = keyid:always 84 | 85 | -------------------------------------------------------------------------------- /tools/openssl/signing-ca.conf: -------------------------------------------------------------------------------- 1 | 2 | oid_section = OIDS 3 | 4 | [ default ] 5 | ca = signing-ca 6 | dir = ./crypto_data 7 | 8 | [ req ] 9 | default_bits = 4096 10 | encrypt_key = yes 11 | default_md = sha256 12 | utf8 = yes 13 | string_mask = utf8only 14 | prompt = no 15 | distinguished_name = ca_dn 16 | req_extensions = ca_reqext 17 | 18 | [ OIDS ] 19 | fido_attestation = 1.3.6.1.4.1.45724.2.1.1 20 | fido_aaguid = 1.3.6.1.4.1.45724.1.1.4 21 | 22 | [ ca_dn ] 23 | countryName = "US" 24 | organizationName = "OpenSK" 25 | organizationalUnitName = "Authenticator Attestation" 26 | commonName = "OpenSK Signing" 27 | 28 | [ ca_reqext ] 29 | keyUsage = critical,keyCertSign,cRLSign 30 | basicConstraints = critical,CA:true,pathlen:0 31 | subjectKeyIdentifier = hash 32 | 33 | [ ca ] 34 | default_ca = signing_ca 35 | 36 | [ signing_ca ] 37 | certificate = $dir/ca/$ca.pem 38 | private_key = $dir/ca/$ca/private/$ca.key 39 | new_certs_dir = $dir/ca/$ca 40 | serial = $dir/ca/$ca/db/$ca.pem.srl 41 | crlnumber = $dir/ca/$ca/db/$ca.pem.srl 42 | database = $dir/ca/$ca/db/$ca.db 43 | unique_subject = no 44 | default_days = 35064 45 | default_md = sha256 46 | policy = match_pol 47 | email_in_dn = no 48 | preserve = no 49 | name_opt = ca_default 50 | cert_opt = ca_default 51 | copy_extensions = copy 52 | x509_extensions = fido_key_ext 53 | default_crl_days = 7 54 | crl_extensions = crl_ext 55 | 56 | [ match_pol ] 57 | countryName = match 58 | organizationName = match 59 | organizationalUnitName = match 60 | commonName = supplied 61 | 62 | [ any_pol ] 63 | countryName = optional 64 | stateOrProvinceName = optional 65 | localityName = optional 66 | organizationName = optional 67 | organizationalUnitName = optional 68 | commonName = optional 69 | emailAddress = optional 70 | 71 | [ root_ca_ext ] 72 | keyUsage = critical,keyCertSign,cRLSign 73 | basicConstraints = critical,CA:true 74 | subjectKeyIdentifier = hash 75 | authorityKeyIdentifier = keyid:always 76 | 77 | [ signing_ca_ext ] 78 | keyUsage = critical,keyCertSign,cRLSign 79 | basicConstraints = critical,CA:true,pathlen:0 80 | subjectKeyIdentifier = hash 81 | authorityKeyIdentifier = keyid:always 82 | 83 | [ fido_key_ext ] 84 | keyUsage = critical,digitalSignature 85 | basicConstraints = CA:false 86 | subjectKeyIdentifier = hash 87 | authorityKeyIdentifier = keyid:always 88 | 89 | [ crl_ext ] 90 | authorityKeyIdentifier = keyid:always 91 | 92 | --------------------------------------------------------------------------------