Compare commits
485 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4339600730 | ||
|
|
298db9ea99 | ||
|
|
ba0d717d88 | ||
|
|
0185d1e443 | ||
|
|
191d043cc4 | ||
|
|
6b8aa3aaf3 | ||
|
|
5fdc6e0739 | ||
|
|
af763450a9 | ||
|
|
eaeb927d92 | ||
|
|
c160b034ef | ||
|
|
6ed83c34c7 | ||
|
|
e5ba9db644 | ||
|
|
3a5adfc5af | ||
|
|
09a6b3ce1a | ||
|
|
778102712f | ||
|
|
67e3d46291 | ||
|
|
8a53986961 | ||
|
|
87f0711284 | ||
|
|
96af5e81a5 | ||
|
|
e3d2e7d778 | ||
|
|
8868752e37 | ||
|
|
a274a512f7 | ||
|
|
f0e87ee813 | ||
|
|
3813cacea7 | ||
|
|
e9ea05f888 | ||
|
|
55f7e47423 | ||
|
|
fbf07d7476 | ||
|
|
6fb7e194eb | ||
|
|
94b0beed4b | ||
|
|
99f81adc55 | ||
|
|
cae2088f36 | ||
|
|
f25cdd6acc | ||
|
|
645c1ba3a7 | ||
|
|
bcd382e5e9 | ||
|
|
a88a1b2a22 | ||
|
|
5f7eb3177b | ||
|
|
3091b5a29d | ||
|
|
a1d6ed0223 | ||
|
|
be42b47caf | ||
|
|
4cc1b4fddf | ||
|
|
d0cdbec5ce | ||
|
|
22192a37d2 | ||
|
|
c168141b60 | ||
|
|
80b82ffd42 | ||
|
|
6d5ea16f2d | ||
|
|
2560b6661c | ||
|
|
752db8cc90 | ||
|
|
ca65902a8f | ||
|
|
03031e6970 | ||
|
|
7769e783bb | ||
|
|
3135c13e6b | ||
|
|
d8512b4417 | ||
|
|
9a2ef0bf75 | ||
|
|
73c60d8740 | ||
|
|
963549f9bb | ||
|
|
98ecdec453 | ||
|
|
a222986995 | ||
|
|
8733d6585f | ||
|
|
684d37fa03 | ||
|
|
0db393bd1e | ||
|
|
6b5f6e53eb | ||
|
|
f6e9e00b87 | ||
|
|
0d0460f016 | ||
|
|
9ab3bc977c | ||
|
|
44cafb9566 | ||
|
|
3c28ff49ee | ||
|
|
6610a29a67 | ||
|
|
f2fac83124 | ||
|
|
d6994e3bc3 | ||
|
|
8288bb0860 | ||
|
|
771ce7635b | ||
|
|
1b360662ee | ||
|
|
598c21071e | ||
|
|
932924ea85 | ||
|
|
01cc8333e5 | ||
|
|
2dc44984ed | ||
|
|
a44d961e7e | ||
|
|
5509e3f072 | ||
|
|
d2037a4bbe | ||
|
|
6bb12252f8 | ||
|
|
e52adf04c7 | ||
|
|
4442998b64 | ||
|
|
87839af572 | ||
|
|
5daf5f81d1 | ||
|
|
d3e01d22fb | ||
|
|
c6d3f51b5f | ||
|
|
f07be7f2ac | ||
|
|
262e505ef7 | ||
|
|
4a2217f025 | ||
|
|
0dad7b19ff | ||
|
|
6276904a42 | ||
|
|
253d27d612 | ||
|
|
2bc405165e | ||
|
|
8ef813cf76 | ||
|
|
9bb1a2f7ac | ||
|
|
168de290de | ||
|
|
fbe00d57f9 | ||
|
|
d2377740ab | ||
|
|
07a28fe611 | ||
|
|
b28f8f6d33 | ||
|
|
4736cc63c4 | ||
|
|
f528567ce1 | ||
|
|
bc354d8abb | ||
|
|
0bbc8663c2 | ||
|
|
25c884c070 | ||
|
|
80a6b82ed7 | ||
|
|
aee7d7c9b3 | ||
|
|
c8dc1dd0e8 | ||
|
|
ecb98b0f58 | ||
|
|
30a3205fa7 | ||
|
|
2095513771 | ||
|
|
0c64dd4778 | ||
|
|
adecf281dd | ||
|
|
8549e2e436 | ||
|
|
ff6c700cd9 | ||
|
|
2256c739cd | ||
|
|
2f9e82696d | ||
|
|
87a4dc725f | ||
|
|
fcdf617a2e | ||
|
|
9a7760f362 | ||
|
|
d793a992d3 | ||
|
|
a9a67ae0d7 | ||
|
|
667c269552 | ||
|
|
55a856fd88 | ||
|
|
41780e9e33 | ||
|
|
e52cafb394 | ||
|
|
7d414439fd | ||
|
|
195ac4a28c | ||
|
|
d7b24424e5 | ||
|
|
90322cf5ed | ||
|
|
b14ed0e742 | ||
|
|
2544afbfee | ||
|
|
1d53f3c921 | ||
|
|
7e0c0938bb | ||
|
|
92e1d51442 | ||
|
|
95aa02f932 | ||
|
|
dc3e34b824 | ||
|
|
4763c3a3f1 | ||
|
|
aea4c56884 | ||
|
|
15233dba3e | ||
|
|
cc1fb2543e | ||
|
|
0158cc846d | ||
|
|
2708074949 | ||
|
|
660b6b76b2 | ||
|
|
4e47968233 | ||
|
|
c0299c3225 | ||
|
|
f2cb2f72e7 | ||
|
|
1cf7373bfe | ||
|
|
db26f6125b | ||
|
|
12f6ed6e0b | ||
|
|
0f368f0b48 | ||
|
|
f57126634b | ||
|
|
85fe9cd29d | ||
|
|
4cfc5f57d4 | ||
|
|
67ef705eb4 | ||
|
|
926410509a | ||
|
|
9a9d68ec41 | ||
|
|
7b872df01e | ||
|
|
5685e95b79 | ||
|
|
9d36da16c7 | ||
|
|
5aac730f93 | ||
|
|
1277b97018 | ||
|
|
e4d3262623 | ||
|
|
6f40c9ad48 | ||
|
|
07424c3123 | ||
|
|
25d538cde6 | ||
|
|
42bfd7860d | ||
|
|
b9c48b480a | ||
|
|
dc7311a3bd | ||
|
|
c7116b1c21 | ||
|
|
06230d15e1 | ||
|
|
245436f135 | ||
|
|
55056b721c | ||
|
|
0ef0bb23f4 | ||
|
|
7f6ff31dd1 | ||
|
|
9713332eff | ||
|
|
3b8884c088 | ||
|
|
e473af7118 | ||
|
|
658dbe2381 | ||
|
|
f24445b325 | ||
|
|
983bc5c5b2 | ||
|
|
f95ae1f5ab | ||
|
|
3a39c4dff1 | ||
|
|
8979af6ca4 | ||
|
|
2b541d853b | ||
|
|
a0e11bd5aa | ||
|
|
4782d7e186 | ||
|
|
360efa4eaf | ||
|
|
397c4165ca | ||
|
|
8dc6dab450 | ||
|
|
2b6424360c | ||
|
|
0f47e99a08 | ||
|
|
aca1f35170 | ||
|
|
1e123ab3c3 | ||
|
|
bbc51af042 | ||
|
|
ee56024206 | ||
|
|
ab67d14e93 | ||
|
|
eb8eccabc4 | ||
|
|
f7d30827a5 | ||
|
|
777623371a | ||
|
|
4da060f799 | ||
|
|
74b472d9cb | ||
|
|
1ef9a4447d | ||
|
|
81996f650e | ||
|
|
2db7971430 | ||
|
|
b33ffb7979 | ||
|
|
742e5f149f | ||
|
|
990c2b3ea6 | ||
|
|
f862d4cc18 | ||
|
|
6e2f076e24 | ||
|
|
8331aa1378 | ||
|
|
0f073f8f54 | ||
|
|
17ecd46b04 | ||
|
|
12c5a419b4 | ||
|
|
c3e1b5df50 | ||
|
|
d6a2080cd5 | ||
|
|
0b564d4a8a | ||
|
|
7e7d5e38a1 | ||
|
|
ba0c583617 | ||
|
|
1372fd0b1a | ||
|
|
163057daf0 | ||
|
|
2050f9f272 | ||
|
|
d81af2857e | ||
|
|
02baff9483 | ||
|
|
0acafb107f | ||
|
|
95ba81b9ed | ||
|
|
b0ddef9e70 | ||
|
|
4edd542b63 | ||
|
|
47470db7a6 | ||
|
|
ca796a5e78 | ||
|
|
bf3d65dc79 | ||
|
|
dc00b94ee8 | ||
|
|
3211342934 | ||
|
|
f08be3d57d | ||
|
|
7c1ddcda0a | ||
|
|
2df7164c1f | ||
|
|
c595980a3b | ||
|
|
3d3689dc23 | ||
|
|
732523d380 | ||
|
|
d1f425c258 | ||
|
|
2e3034193e | ||
|
|
f09e5a77e8 | ||
|
|
dcc053c6cb | ||
|
|
76b249c415 | ||
|
|
d16811fe25 | ||
|
|
91ba2c375e | ||
|
|
bb40e3244a | ||
|
|
b80b67e2cf | ||
|
|
172e629987 | ||
|
|
3ebc63e964 | ||
|
|
187111f9c5 | ||
|
|
d476e58612 | ||
|
|
74c6c3da74 | ||
|
|
c401216544 | ||
|
|
c4a27bf935 | ||
|
|
d6e4c66562 | ||
|
|
5c59e809c2 | ||
|
|
7d39d4e2e8 | ||
|
|
6b8523ba93 | ||
|
|
18faf9f38f | ||
|
|
8a2e99960f | ||
|
|
d47ca7fa54 | ||
|
|
b59df7001f | ||
|
|
98c9191679 | ||
|
|
ce08f82d68 | ||
|
|
ca2ea2007e | ||
|
|
83b2a74ae6 | ||
|
|
37e9d6d64d | ||
|
|
522e6079e3 | ||
|
|
8f96df53f2 | ||
|
|
402b708ab9 | ||
|
|
5cf988c7fa | ||
|
|
ae4e32ba4a | ||
|
|
40e912f8ac | ||
|
|
32da73772f | ||
|
|
33e0d6bb74 | ||
|
|
330fa12d1a | ||
|
|
44988695ab | ||
|
|
4eb7f02985 | ||
|
|
9b780ef7d7 | ||
|
|
d085d54878 | ||
|
|
af3bee64a5 | ||
|
|
9dc5286633 | ||
|
|
daa16d948f | ||
|
|
67fa8bee0b | ||
|
|
a3965eac2d | ||
|
|
fbca34b1d1 | ||
|
|
0f88d6502f | ||
|
|
930a44c105 | ||
|
|
1adde220c4 | ||
|
|
31df2ca45e | ||
|
|
18ba4368e4 | ||
|
|
596b47886c | ||
|
|
7a975acf33 | ||
|
|
2d5fdd1034 | ||
|
|
c1f2551d0d | ||
|
|
c2b3aeca88 | ||
|
|
c6af7c0a2d | ||
|
|
7418196814 | ||
|
|
cbbb4b3e08 | ||
|
|
ed28941a6d | ||
|
|
a80ff4279c | ||
|
|
b5b9d3f6e0 | ||
|
|
659f8a16a2 | ||
|
|
ec994eac32 | ||
|
|
53da98c272 | ||
|
|
0f70a211ea | ||
|
|
7a812a657b | ||
|
|
d25f65c565 | ||
|
|
69f1b672f1 | ||
|
|
ad0605c2fa | ||
|
|
3d4b652e12 | ||
|
|
445c1c6edd | ||
|
|
b7a3e06cf4 | ||
|
|
7bb4960730 | ||
|
|
f5de994ad4 | ||
|
|
58ae1ac8b1 | ||
|
|
14115fbc79 | ||
|
|
a532959e8f | ||
|
|
146b54e9d0 | ||
|
|
826c4f3021 | ||
|
|
eefc171076 | ||
|
|
46bbef2996 | ||
|
|
74d712da0d | ||
|
|
ce0ee6c054 | ||
|
|
5f20ba544b | ||
|
|
0287a09573 | ||
|
|
dbce426e9f | ||
|
|
9ca17b17e1 | ||
|
|
c7750a4e8c | ||
|
|
1cee2414f4 | ||
|
|
fbe68b55cd | ||
|
|
3aca5fbc74 | ||
|
|
7719078d46 | ||
|
|
f2812e4fe2 | ||
|
|
77f6db6110 | ||
|
|
fe0a9f208e | ||
|
|
c86905f592 | ||
|
|
d9e32ac103 | ||
|
|
6aa6a8acf0 | ||
|
|
67311e6c9f | ||
|
|
b3b652aa53 | ||
|
|
bb4b94a141 | ||
|
|
67c4b3d158 | ||
|
|
e32eb5358f | ||
|
|
c03605aa0c | ||
|
|
7c8894bb04 | ||
|
|
9a1c060234 | ||
|
|
e9c66a2764 | ||
|
|
2957c800cd | ||
|
|
78b7767682 | ||
|
|
054e303d11 | ||
|
|
6216a3214d | ||
|
|
e7797a5683 | ||
|
|
c596f785ff | ||
|
|
63232cfe60 | ||
|
|
aec1e0a409 | ||
|
|
b1773d1cf3 | ||
|
|
e50d89e28b | ||
|
|
e5313057f9 | ||
|
|
6cb6538db6 | ||
|
|
3c7c5a4810 | ||
|
|
eb0a0770dd | ||
|
|
351e6c12c6 | ||
|
|
5e9c32dff5 | ||
|
|
f11a838cc7 | ||
|
|
c014d21ff8 | ||
|
|
958d7a29dc | ||
|
|
6480682d95 | ||
|
|
6a31e06a55 | ||
|
|
4678a7417d | ||
|
|
b9072047b3 | ||
|
|
160c83d242 | ||
|
|
48ee857850 | ||
|
|
88a3c0fc80 | ||
|
|
e941073a31 | ||
|
|
70ba53ca46 | ||
|
|
800f0be771 | ||
|
|
54e9da7a5b | ||
|
|
842c592c9f | ||
|
|
f90d43a6a1 | ||
|
|
604f084815 | ||
|
|
f2fe411d77 | ||
|
|
502006e29e | ||
|
|
a54b217116 | ||
|
|
c293708649 | ||
|
|
49cccfd270 | ||
|
|
53e0591363 | ||
|
|
44b7c3cdc1 | ||
|
|
e3148319c5 | ||
|
|
db7ed10f5f | ||
|
|
b32d92d9e2 | ||
|
|
f64567febc | ||
|
|
9270afbc21 | ||
|
|
371e8b6f35 | ||
|
|
5683b455b2 | ||
|
|
5741595e57 | ||
|
|
151a37eb47 | ||
|
|
2dbe1c5f07 | ||
|
|
0f85470960 | ||
|
|
18f391d48f | ||
|
|
3346a1167e | ||
|
|
769a2ae1c5 | ||
|
|
2af85ad9d0 | ||
|
|
49de1f7ebc | ||
|
|
4f3c773b15 | ||
|
|
c8cdbd61e4 | ||
|
|
36be5d8a74 | ||
|
|
563f35184a | ||
|
|
ae0156d287 | ||
|
|
0e537733f1 | ||
|
|
b2c6ae8f82 | ||
|
|
846ff279bb | ||
|
|
41a3f512c8 | ||
|
|
f0c51950cb | ||
|
|
19c089e955 | ||
|
|
7d04c5c6d0 | ||
|
|
cf8b54b39c | ||
|
|
3517b1163d | ||
|
|
b2c8c5a128 | ||
|
|
d87d35847a | ||
|
|
c38f00624a | ||
|
|
5fe111698b | ||
|
|
3408c0a2ed | ||
|
|
de3addba74 | ||
|
|
cdde64420b | ||
|
|
14189a398a | ||
|
|
03401778b3 | ||
|
|
8634e2ec24 | ||
|
|
8bdfeb4aec | ||
|
|
6bf4a7edec | ||
|
|
9296f51e19 | ||
|
|
9953b3f1a0 | ||
|
|
134c880212 | ||
|
|
e3353cb232 | ||
|
|
286f70ef1c | ||
|
|
a712d1476b | ||
|
|
51ecf6acc1 | ||
|
|
55038cc084 | ||
|
|
2cd760bad7 | ||
|
|
3702b61ce7 | ||
|
|
aef9566ca4 | ||
|
|
5818c3f6af | ||
|
|
69bdd8c615 | ||
|
|
7268a9474b | ||
|
|
0bb6ee32fc | ||
|
|
1f37ae50c5 | ||
|
|
d5761018ab | ||
|
|
e545acda16 | ||
|
|
182afc7c3f | ||
|
|
a17ee39bb6 | ||
|
|
c6726660ac | ||
|
|
46b9a0262c | ||
|
|
3e42531011 | ||
|
|
a26de3b720 | ||
|
|
cc86fc2742 | ||
|
|
78167282f9 | ||
|
|
c30268a099 | ||
|
|
da27848c27 | ||
|
|
a82f767c18 | ||
|
|
2776bd9b8e | ||
|
|
688d11c6b6 | ||
|
|
499816069e | ||
|
|
27a7108328 | ||
|
|
4cee0c4c65 | ||
|
|
18ebeebb3e | ||
|
|
6f9f833c0b | ||
|
|
ec259d8428 | ||
|
|
f4eb6c938e | ||
|
|
32d5ff91d4 | ||
|
|
50611d62db | ||
|
|
da03f77a32 | ||
|
|
caefc7553f | ||
|
|
c873d3b614 | ||
|
|
de360a6cb6 | ||
|
|
deeabe026f | ||
|
|
a836aec464 | ||
|
|
ea9d3cfadb | ||
|
|
f67fdbc451 | ||
|
|
4530455638 | ||
|
|
d6adab4381 | ||
|
|
1d576fdd31 | ||
|
|
fb15032f0b | ||
|
|
edcc206e9d | ||
|
|
d23acb4f64 |
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,5 +2,10 @@ Fixes #<issue_number_goes_here>
|
||||
|
||||
> It's a good idea to open an issue first for discussion.
|
||||
|
||||
- [ ] Tests pass
|
||||
- [ ] Appropriate changes to README are included in PR
|
||||
- [ ] Local tests pass (running `run_desktop_tests.sh`)
|
||||
- [ ] Tested against boards
|
||||
- [ ] Nordic nRF52840 DK
|
||||
- [ ] Nordic nRF52840 Dongle (JTAG programmed)
|
||||
- [ ] Nordic nRF52840 Dongle (DFU programmed)
|
||||
- [ ] Makerdiary nRF52840 MDK USB Dongle
|
||||
- [ ] Appropriate changes to README are included in PR
|
||||
|
||||
9
.github/actions-rs/grcov.yml
vendored
9
.github/actions-rs/grcov.yml
vendored
@@ -1,9 +0,0 @@
|
||||
branch: true
|
||||
ignore-not-existing: true
|
||||
llvm: true
|
||||
filter: covered
|
||||
output-type: lcov
|
||||
output-path: ./lcov.info
|
||||
ignore:
|
||||
- "third_party/*"
|
||||
- "/*"
|
||||
46
.github/workflows/bloat_formatter.sh
vendored
Executable file
46
.github/workflows/bloat_formatter.sh
vendored
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# New output file is $1
|
||||
# Old output file is $2
|
||||
TMP=comment.md
|
||||
WARNING="Note: numbers above are a result of guesswork. They are not 100% correct and never will be."
|
||||
NEW_SIZE=$(cat "$1" | sed -nr 's/.*100.0% (.*)KiB .text.*/\1/p')
|
||||
OLD_SIZE=$(cat "$2" | sed -nr 's/.*100.0% (.*)KiB .text.*/\1/p')
|
||||
|
||||
echo "
|
||||
OLD $OLD_SIZE kiB
|
||||
NEW $NEW_SIZE kiB" > "$TMP"
|
||||
|
||||
echo "
|
||||
Output of cargo bloat
|
||||
======================
|
||||
" >> "$TMP"
|
||||
|
||||
echo "Including PR" >> "$TMP"
|
||||
cat "$1" >> "$TMP"
|
||||
echo "Base branch" >> "$TMP"
|
||||
cat "$2" >> "$TMP"
|
||||
|
||||
COMMENT="$(cat $TMP | sed "s/$WARNING//g" | sed 's/%/%25/g' | sed -z 's/\n/%0A/g')"
|
||||
# No output for equality is intentional.
|
||||
if (( $(echo "$NEW_SIZE > $OLD_SIZE" | bc -l) )); then
|
||||
echo "::warning file=.github/workflows/cargo_bloat.yml,title=Binary size::$COMMENT"
|
||||
fi
|
||||
if (( $(echo "$NEW_SIZE < $OLD_SIZE" | bc -l) )); then
|
||||
echo "::notice file=.github/workflows/cargo_bloat.yml,title=Binary size::$COMMENT"
|
||||
fi
|
||||
21
.github/workflows/cargo_audit.yml
vendored
Normal file
21
.github/workflows/cargo_audit.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Security audit
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'google/OpenSK'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "true"
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Set up OpenSK
|
||||
run: ./setup.sh
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
40
.github/workflows/cargo_bloat.yml
vendored
Normal file
40
.github/workflows/cargo_bloat.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: Binary size report
|
||||
on: pull_request
|
||||
|
||||
jobs:
|
||||
cargo_bloat:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Setup
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: install
|
||||
args: cargo-bloat
|
||||
|
||||
# First run: PR
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Set up OpenSK
|
||||
run: ./setup.sh
|
||||
- name: Run bloat on the PR
|
||||
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
|
||||
|
||||
# Second run: PR
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
ref: ${{ github.base_ref }}
|
||||
path: OpenSK_base
|
||||
- name: Set up OpenSK
|
||||
working-directory: ./OpenSK_base
|
||||
run: ./setup.sh
|
||||
- name: Run bloat on base
|
||||
working-directory: ./OpenSK_base
|
||||
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"
|
||||
|
||||
- name: Run output formatter to echo workflow command
|
||||
run: ./.github/workflows/bloat_formatter.sh bloat_output_new.txt bloat_output_old.txt bloat_comment.md
|
||||
31
.github/workflows/ci.yml
vendored
Normal file
31
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Continuous Integration
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
pull_request:
|
||||
branches:
|
||||
- develop
|
||||
schedule:
|
||||
- cron: 30 1 * * 2 # every Tuesday at 1:30 UTC
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
runtests:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- run: ./setup.sh
|
||||
- run: ./run_desktop_tests.sh
|
||||
28
.github/workflows/cifuzz.yml
vendored
Normal file
28
.github/workflows/cifuzz.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: CIFuzz
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build Fuzzers
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'opensk'
|
||||
dry-run: false
|
||||
language: rust
|
||||
- name: Run Fuzzers
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'opensk'
|
||||
fuzz-seconds: 600
|
||||
dry-run: false
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out/artifacts
|
||||
47
.github/workflows/coveralls.yml
vendored
Normal file
47
.github/workflows/coveralls.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
name: OpenSK code coverage report
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'libraries/**/*.rs'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
coveralls:
|
||||
name: OpenSK code coverage
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "true"
|
||||
- name: Install Rust toolchain
|
||||
run: rustup show
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Set up OpenSK
|
||||
run: ./setup.sh
|
||||
- name: Install llvm tools
|
||||
run: rustup +nightly component add llvm-tools-preview
|
||||
|
||||
- name: Install grcov
|
||||
run: if [[ ! -e ~/.cargo/bin/grcov ]]; then cargo +stable install grcov; fi
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
command: test
|
||||
args: --manifest-path libraries/opensk/Cargo.toml --features "std,with_ctap1,vendor_hid,ed25519" --no-fail-fast
|
||||
env:
|
||||
RUSTFLAGS: "-Cinstrument-coverage"
|
||||
LLVM_PROFILE_FILE: "opensk-%p-%m.profraw"
|
||||
- name: Run grcov
|
||||
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/*"
|
||||
- uses: coverallsapp/github-action@1.1.3
|
||||
name: upload report to coveralls
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
path-to-lcov: "./lcov.info"
|
||||
base-path: "libraries/opensk"
|
||||
|
||||
8
.github/workflows/mdlint.yml
vendored
8
.github/workflows/mdlint.yml
vendored
@@ -12,10 +12,10 @@ jobs:
|
||||
mdlint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: markdownlint-cli
|
||||
uses: nosborn/github-action-markdown-cli@v1.1.1
|
||||
uses: nosborn/github-action-markdown-cli@v3
|
||||
with:
|
||||
files: '**/*.md'
|
||||
ignore_files: "third_party/*"
|
||||
config_file: ".markdownlint.json"
|
||||
config_file: '.markdownlint.json'
|
||||
ignore_files: 'third_party/*'
|
||||
|
||||
43
.github/workflows/reproducible.yml
vendored
Normal file
43
.github/workflows/reproducible.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
name: Check that binaries are reproducible
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
check_hashes:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "true"
|
||||
- name: Install Rust toolchain
|
||||
run: rustup show
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Set up OpenSK
|
||||
run: ./setup.sh
|
||||
|
||||
- name: Use sample cryptographic material
|
||||
run: rm -R crypto_data/ && cp -r reproducible/sample_crypto_data crypto_data
|
||||
- name: Computing cryptographic hashes
|
||||
run: ./maintainers/reproduce_hashes.sh
|
||||
|
||||
- name: Upload reproduced binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: reproduced-${{ matrix.os }}
|
||||
path: reproducible/reproduced.tar
|
||||
|
||||
- name: Comparing binary sizes
|
||||
if: always()
|
||||
run: git diff --no-index reproducible/reference_elf2tab_${{ matrix.os }}.txt reproducible/elf2tab.txt || true
|
||||
- name: Comparing cryptographic hashes
|
||||
if: always()
|
||||
run: git diff --no-index reproducible/reference_binaries_${{ matrix.os }}.sha256sum reproducible/binaries.sha256sum || true
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,7 @@
|
||||
libraries/**/Cargo.lock
|
||||
target/
|
||||
/build/
|
||||
/py_virtual_env/
|
||||
|
||||
# Local installation of elf2tab.
|
||||
/elf2tab/
|
||||
|
||||
@@ -1,35 +1,22 @@
|
||||
{
|
||||
"default": true,
|
||||
"heading-style": {
|
||||
"MD003": {
|
||||
"style": "atx"
|
||||
},
|
||||
"no-trailing-spaces": {
|
||||
"MD007": {
|
||||
"indent": 4
|
||||
},
|
||||
"MD009": {
|
||||
"br_spaces": 0,
|
||||
"strict": true
|
||||
},
|
||||
"ul-indent": {
|
||||
"indent": 4
|
||||
},
|
||||
"line-length": {
|
||||
"MD013": {
|
||||
"line_length": 80,
|
||||
"code_blocks": false
|
||||
},
|
||||
"list-marker-space": {
|
||||
"ol_single": 2,
|
||||
"ol_multi": 2,
|
||||
"ul_single": 3,
|
||||
"ul_multi": 3
|
||||
},
|
||||
"no-inline-html": {
|
||||
"MD033": {
|
||||
"allowed_elements": [
|
||||
"img"
|
||||
]
|
||||
},
|
||||
"fenced-code-language": true,
|
||||
"code-block-style": {
|
||||
"style": "fenced"
|
||||
},
|
||||
"code-fence-style": {
|
||||
"style": "backtick"
|
||||
"img"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
545
.pylintrc
545
.pylintrc
@@ -1,168 +1,266 @@
|
||||
# File taken from Tensor2Tensor project
|
||||
# https://github.com/tensorflow/tensor2tensor/blob/master/pylintrc
|
||||
# This Pylint rcfile contains a best-effort configuration to uphold the
|
||||
# best-practices and style described in the Google Python style guide:
|
||||
# https://google.github.io/styleguide/pyguide.html
|
||||
#
|
||||
# Its canonical open-source location is:
|
||||
# https://google.github.io/styleguide/pylintrc
|
||||
|
||||
[MASTER]
|
||||
[MAIN]
|
||||
|
||||
# Files or directories to be skipped. They should be base names, not paths.
|
||||
ignore=third_party
|
||||
|
||||
# Files or directories matching the regex patterns are skipped. The regex
|
||||
# matches against base names, not paths.
|
||||
ignore-patterns=
|
||||
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=no
|
||||
|
||||
# Set the cache size for astng objects.
|
||||
cache-size=500
|
||||
|
||||
# Ignore Py3 files
|
||||
ignore=get_references_web.py,get_references_web_single_group.py
|
||||
|
||||
# List of plugins (as comma separated values of python modules names) to load,
|
||||
# usually to register additional checkers.
|
||||
load-plugins=
|
||||
pylint.extensions.bad_builtin,
|
||||
pylint.extensions.docparams,
|
||||
pylint.extensions.docstyle,
|
||||
pylint.extensions.redefined_variable_type,
|
||||
pylint.extensions.overlapping_exceptions,
|
||||
|
||||
# Use multiple processes to speed up Pylint.
|
||||
jobs=4
|
||||
|
||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
||||
# active Python interpreter and may run arbitrary code.
|
||||
unsafe-load-any-extension=no
|
||||
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
|
||||
confidence=
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time (only on the command line, not in the configuration file where
|
||||
# it should appear only once). See also the "--disable" option for examples.
|
||||
#enable=
|
||||
|
||||
# Disable the message, report, category or checker with the given id(s). You
|
||||
# can either give multiple identifiers separated by comma (,) or put this
|
||||
# option multiple times (only on the command line, not in the configuration
|
||||
# file where it should appear only once).You can also use "--disable=all" to
|
||||
# disable everything first and then reenable specific checks. For example, if
|
||||
# you want to run only the similarities checker, you can use "--disable=all
|
||||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use"--disable=all --enable=classes
|
||||
# --disable=W"
|
||||
disable=abstract-method,
|
||||
apply-builtin,
|
||||
arguments-differ,
|
||||
attribute-defined-outside-init,
|
||||
backtick,
|
||||
bad-option-value,
|
||||
basestring-builtin,
|
||||
buffer-builtin,
|
||||
c-extension-no-member,
|
||||
consider-using-enumerate,
|
||||
cmp-builtin,
|
||||
cmp-method,
|
||||
coerce-builtin,
|
||||
coerce-method,
|
||||
delslice-method,
|
||||
div-method,
|
||||
duplicate-code,
|
||||
eq-without-hash,
|
||||
execfile-builtin,
|
||||
file-builtin,
|
||||
filter-builtin-not-iterating,
|
||||
fixme,
|
||||
getslice-method,
|
||||
global-statement,
|
||||
hex-method,
|
||||
idiv-method,
|
||||
implicit-str-concat-in-sequence,
|
||||
import-error,
|
||||
import-self,
|
||||
import-star-module-level,
|
||||
inconsistent-return-statements,
|
||||
input-builtin,
|
||||
intern-builtin,
|
||||
invalid-str-codec,
|
||||
locally-disabled,
|
||||
long-builtin,
|
||||
long-suffix,
|
||||
map-builtin-not-iterating,
|
||||
misplaced-comparison-constant,
|
||||
missing-function-docstring,
|
||||
metaclass-assignment,
|
||||
next-method-called,
|
||||
next-method-defined,
|
||||
no-absolute-import,
|
||||
no-else-break,
|
||||
no-else-continue,
|
||||
no-else-raise,
|
||||
no-else-return,
|
||||
no-init, # added
|
||||
no-member,
|
||||
no-name-in-module,
|
||||
no-self-use,
|
||||
nonzero-method,
|
||||
oct-method,
|
||||
old-division,
|
||||
old-ne-operator,
|
||||
old-octal-literal,
|
||||
old-raise-syntax,
|
||||
parameter-unpacking,
|
||||
print-statement,
|
||||
raising-string,
|
||||
range-builtin-not-iterating,
|
||||
raw_input-builtin,
|
||||
rdiv-method,
|
||||
reduce-builtin,
|
||||
relative-import,
|
||||
reload-builtin,
|
||||
round-builtin,
|
||||
setslice-method,
|
||||
signature-differs,
|
||||
standarderror-builtin,
|
||||
suppressed-message,
|
||||
sys-max-int,
|
||||
too-few-public-methods,
|
||||
too-many-ancestors,
|
||||
too-many-arguments,
|
||||
too-many-boolean-expressions,
|
||||
too-many-branches,
|
||||
too-many-instance-attributes,
|
||||
too-many-locals,
|
||||
too-many-nested-blocks,
|
||||
too-many-public-methods,
|
||||
too-many-return-statements,
|
||||
too-many-statements,
|
||||
trailing-newlines,
|
||||
unichr-builtin,
|
||||
unicode-builtin,
|
||||
unnecessary-pass,
|
||||
unpacking-in-except,
|
||||
unrecognized-option,
|
||||
useless-else-on-loop,
|
||||
useless-object-inheritance,
|
||||
useless-suppression,
|
||||
using-cmp-argument,
|
||||
wrong-import-order,
|
||||
xrange-builtin,
|
||||
zip-builtin-not-iterating,
|
||||
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Set the output format.
|
||||
# output-format=sorted-text
|
||||
# Set the output format. Available formats are text, parseable, colorized, msvs
|
||||
# (visual studio) and html. You can also give a reporter class, eg
|
||||
# mypackage.mymodule.MyReporterClass.
|
||||
output-format=text
|
||||
|
||||
# Put messages in a separate file for each module / package specified on the
|
||||
# command line instead of printing them on stdout. Reports (if any) will be
|
||||
# written in a file name "pylint_global.[txt|html]".
|
||||
# written in a file name "pylint_global.[txt|html]". This option is deprecated
|
||||
# and it will be removed in Pylint 2.0.
|
||||
files-output=no
|
||||
|
||||
# Tells whether to display a full report or only the messages.
|
||||
# Tells whether to display a full report or only the messages
|
||||
reports=no
|
||||
|
||||
# Disable the report(s) with the given id(s).
|
||||
disable-report=R0001,R0002,R0003,R0004,R0101,R0102,R0201,R0202,R0220,R0401,R0402,R0701,R0801,R0901,R0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R0921,R0922,R0923
|
||||
# Python expression which should return a note less than 10 (10 is the highest
|
||||
# note). You have access to the variables errors warning, statement which
|
||||
# respectively contain the number of errors / warnings messages and the total
|
||||
# number of statements analyzed. This is used by the global evaluation report
|
||||
# (RP0004).
|
||||
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||
|
||||
# Error message template (continued on second line)
|
||||
msg-template={msg_id}:{line:3} {obj}: {msg} [{symbol}]
|
||||
# Template used to display messages. This is a python new-style format string
|
||||
# used to format the message information. See doc for all details
|
||||
#msg-template=
|
||||
|
||||
# We don't need evaluation score
|
||||
score=no
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
# List of checkers and warnings to enable.
|
||||
enable=indexing-exception,old-raise-syntax
|
||||
|
||||
# List of checkers and warnings to disable.
|
||||
disable=design,similarities,no-self-use,attribute-defined-outside-init,locally-disabled,star-args,pointless-except,bad-option-value,global-statement,fixme,suppressed-message,useless-suppression,locally-enabled,file-ignored,multiple-imports,c-extension-no-member,trailing-newlines,unsubscriptable-object,misplaced-comparison-constant,no-member,abstract-method,no-else-return,missing-docstring,wrong-import-order,protected-access,inconsistent-return-statements,invalid-unary-operand-type,import-error,no-name-in-module,arguments-differ,not-context-manager,unused-argument
|
||||
|
||||
[BASIC]
|
||||
|
||||
# Required attributes for module, separated by a comma
|
||||
required-attributes=
|
||||
|
||||
# Regular expression which should only match the name
|
||||
# of functions or classes which do not require a docstring.
|
||||
no-docstring-rgx=(__.*__|main)
|
||||
|
||||
# Min length in lines of a function that requires a docstring.
|
||||
docstring-min-length=10
|
||||
|
||||
# Regular expression which should only match correct module names. The
|
||||
# leading underscore is sanctioned for private modules by Google's style
|
||||
# guide.
|
||||
#
|
||||
# There are exceptions to the basic rule (_?[a-z][a-z0-9_]*) to cover
|
||||
# requirements of Python's module system.
|
||||
module-rgx=^(_?[a-z][a-z0-9_]*)|__init__$
|
||||
|
||||
# Regular expression which should only match correct module level names
|
||||
const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
|
||||
|
||||
# Regular expression which should only match correct class attribute
|
||||
class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
|
||||
|
||||
# Regular expression which should only match correct class names
|
||||
class-rgx=^_?[A-Z][a-zA-Z0-9]*$
|
||||
|
||||
# Regular expression which should only match correct function names.
|
||||
# 'camel_case' and 'snake_case' group names are used for consistency of naming
|
||||
# styles across functions and methods.
|
||||
function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_?[a-z][a-z0-9_]*))$
|
||||
|
||||
|
||||
# Regular expression which should only match correct method names.
|
||||
# 'camel_case' and 'snake_case' group names are used for consistency of naming
|
||||
# styles across functions and methods. 'exempt' indicates a name which is
|
||||
# consistent with all naming styles.
|
||||
method-rgx=(?x)
|
||||
^(?:(?P<exempt>_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase
|
||||
|tearDownTestCase|setupSelf|tearDownClass|setUpClass
|
||||
|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)
|
||||
|(?P<camel_case>_{0,2}[A-Z][a-zA-Z0-9_]*)
|
||||
|(?P<snake_case>_{0,2}[a-z][a-z0-9_]*))$
|
||||
|
||||
|
||||
# Regular expression which should only match correct instance attribute names
|
||||
attr-rgx=^_{0,2}[a-z][a-z0-9_]*$
|
||||
|
||||
# Regular expression which should only match correct argument names
|
||||
argument-rgx=^[a-z][a-z0-9_]*$
|
||||
|
||||
# Regular expression which should only match correct variable names
|
||||
variable-rgx=^[a-z][a-z0-9_]*$
|
||||
|
||||
# Regular expression which should only match correct list comprehension /
|
||||
# generator expression variable names
|
||||
inlinevar-rgx=^[a-z][a-z0-9_]*$
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma
|
||||
good-names=main,_
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma
|
||||
bad-names=
|
||||
|
||||
# List of builtins function names that should not be used, separated by a comma
|
||||
bad-functions=input,apply,reduce
|
||||
# Colon-delimited sets of names that determine each other's naming style when
|
||||
# the name regexes allow several styles.
|
||||
name-group=
|
||||
|
||||
# List of decorators that define properties, such as abc.abstractproperty.
|
||||
property-classes=abc.abstractproperty
|
||||
# Include a hint for the correct naming format with invalid-name
|
||||
include-naming-hint=no
|
||||
|
||||
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
||||
# to this list to register other decorators that produce valid properties.
|
||||
property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl
|
||||
|
||||
# Regular expression matching correct function names
|
||||
function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_?[a-z][a-z0-9_]*))$
|
||||
|
||||
# Regular expression matching correct variable names
|
||||
variable-rgx=^[a-z][a-z0-9_]*$
|
||||
|
||||
# Regular expression matching correct constant names
|
||||
const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
|
||||
|
||||
# Regular expression matching correct attribute names
|
||||
attr-rgx=^_{0,2}[a-z][a-z0-9_]*$
|
||||
|
||||
# Regular expression matching correct argument names
|
||||
argument-rgx=^[a-z][a-z0-9_]*$
|
||||
|
||||
# Regular expression matching correct class attribute names
|
||||
class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
|
||||
|
||||
# Regular expression matching correct inline iteration names
|
||||
inlinevar-rgx=^[a-z][a-z0-9_]*$
|
||||
|
||||
# Regular expression matching correct class names
|
||||
class-rgx=^_?[A-Z][a-zA-Z0-9]*$
|
||||
|
||||
# Regular expression matching correct module names
|
||||
module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$
|
||||
|
||||
# Regular expression matching correct method names
|
||||
method-rgx=(?x)^(?:(?P<exempt>_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P<camel_case>_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P<snake_case>_{0,2}[a-z][a-z0-9_]*))$
|
||||
|
||||
# Regular expression which should only match function or class names that do
|
||||
# not require a docstring.
|
||||
no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$
|
||||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
docstring-min-length=10
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
||||
# List of decorators that produce context managers, such as
|
||||
# contextlib.contextmanager. Add to this list to register other decorators that
|
||||
# produce valid context managers.
|
||||
contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager
|
||||
|
||||
# Tells whether missing members accessed in mixin class should be ignored. A
|
||||
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||
ignore-mixin-members=yes
|
||||
|
||||
# List of decorators that create context managers from functions, such as
|
||||
# contextlib.contextmanager.
|
||||
contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager
|
||||
# List of module names for which member attributes should not be checked
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis. It
|
||||
# supports qualified module names, as well as Unix pattern matching.
|
||||
ignored-modules=
|
||||
|
||||
# List of class names for which member attributes should not be checked (useful
|
||||
# for classes with dynamically set attributes). This supports the use of
|
||||
# qualified names.
|
||||
ignored-classes=optparse.Values,thread._local,_thread._local
|
||||
|
||||
[VARIABLES]
|
||||
|
||||
# Tells whether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# A regular expression matching names used for dummy variables (i.e. not used).
|
||||
dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_)
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
|
||||
[CLASSES]
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,__new__,setUp
|
||||
|
||||
# "class_" is also a valid for the first argument to a class method.
|
||||
valid-classmethod-first-arg=cls,class_
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
overgeneral-exceptions=StandardError,Exception,BaseException
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma
|
||||
deprecated-modules=regsub,TERMIOS,Bastion,rexec,sets
|
||||
# List of members which are set dynamically and missed by pylint inference
|
||||
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
||||
# expressions are accepted.
|
||||
generated-members=
|
||||
|
||||
|
||||
[FORMAT]
|
||||
@@ -170,66 +268,175 @@ deprecated-modules=regsub,TERMIOS,Bastion,rexec,sets
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=80
|
||||
|
||||
# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt
|
||||
# lines made too long by directives to pytype.
|
||||
|
||||
# Regexp for a line that is allowed to be longer than the limit.
|
||||
# This "ignore" regex is today composed of several independent parts:
|
||||
# (1) Long import lines
|
||||
# (2) URLs in comments or pydocs. Detecting URLs by regex is a hard problem and
|
||||
# no amount of tweaking will make a perfect regex AFAICT. This one is a good
|
||||
# compromise.
|
||||
# (3) Constant string literals at the start of files don't need to be broken
|
||||
# across lines. Allowing long paths and urls to be on a single
|
||||
# line. Also requires that the string not be a triplequoted string.
|
||||
ignore-long-lines=(?x)
|
||||
(^\s*(import|from)\s
|
||||
|^\s*(\#\ )?<?(https?|ftp):\/\/[^\s\/$.?#].[^\s]*>?$
|
||||
|^[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*("[^"]\S+"|'[^']\S+')
|
||||
)
|
||||
ignore-long-lines=(?x)(
|
||||
^\s*(\#\ )?<?https?://\S+>?$|
|
||||
^\s*(from\s+\S+\s+)?import\s+.+$)
|
||||
|
||||
# Allow the body of an if to be on the same line as the test if there is no
|
||||
# else.
|
||||
single-line-if-stmt=yes
|
||||
|
||||
# List of optional constructs for which whitespace checking is disabled. `dict-
|
||||
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
|
||||
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
|
||||
# `empty-line` allows space-only lines.
|
||||
no-space-check=
|
||||
|
||||
# Maximum number of lines in a module
|
||||
max-module-lines=99999
|
||||
|
||||
# String used as indentation unit. We differ from PEP8's normal 4 spaces.
|
||||
# String used as indentation unit. The internal Google style guide mandates 2
|
||||
# spaces. Google's externaly-published style guide says 4, consistent with
|
||||
# PEP 8. Here, we use 2 spaces, for conformity with many open-sourced Google
|
||||
# projects (like TensorFlow).
|
||||
indent-string=' '
|
||||
|
||||
# Do not warn about multiple statements on a single line for constructs like
|
||||
# if test: stmt
|
||||
single-line-if-stmt=y
|
||||
# Number of spaces of indent required inside a hanging or continued line.
|
||||
indent-after-paren=4
|
||||
|
||||
# Make sure : in dicts and trailing commas are checked for whitespace.
|
||||
no-space-check=
|
||||
|
||||
|
||||
[LOGGING]
|
||||
|
||||
# Add logging modules.
|
||||
logging-modules=logging,absl.logging
|
||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
||||
expected-line-ending-format=
|
||||
|
||||
|
||||
[MISCELLANEOUS]
|
||||
|
||||
# List of note tags to take in consideration, separated by a comma.
|
||||
notes=
|
||||
notes=TODO
|
||||
|
||||
|
||||
# Maximum line length for lambdas
|
||||
short-func-length=1
|
||||
[STRING]
|
||||
|
||||
# List of module members that should be marked as deprecated.
|
||||
# All of the string functions are listed in 4.1.4 Deprecated string functions
|
||||
# in the Python 2.4 docs.
|
||||
deprecated-members=string.atof,string.atoi,string.atol,string.capitalize,string.expandtabs,string.find,string.rfind,string.index,string.rindex,string.count,string.lower,string.split,string.rsplit,string.splitfields,string.join,string.joinfields,string.lstrip,string.rstrip,string.strip,string.swapcase,string.translate,string.upper,string.ljust,string.rjust,string.center,string.zfill,string.replace,sys.exitfunc,sys.maxint
|
||||
# This flag controls whether inconsistent-quotes generates a warning when the
|
||||
# character used as a quote delimiter is used inconsistently within a module.
|
||||
check-quote-consistency=yes
|
||||
|
||||
|
||||
# List of exceptions that do not need to be mentioned in the Raises section of
|
||||
# a docstring.
|
||||
ignore-exceptions=AssertionError,NotImplementedError,StopIteration,TypeError
|
||||
[VARIABLES]
|
||||
|
||||
# Tells whether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# A regular expression matching the name of dummy variables (i.e. expectedly
|
||||
# not used).
|
||||
dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_)
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
# List of strings which can identify a callback function by name. A callback
|
||||
# name must start or end with one of those strings.
|
||||
callbacks=cb_,_cb
|
||||
|
||||
# List of qualified module names which can have objects that can redefine
|
||||
# builtins.
|
||||
redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools
|
||||
|
||||
|
||||
# Number of spaces of indent required when the last token on the preceding line
|
||||
# is an open (, [, or {.
|
||||
indent-after-paren=4
|
||||
[LOGGING]
|
||||
|
||||
# Set the linting for string quotes
|
||||
string-quote=double
|
||||
triple-quote=double
|
||||
docstring-quote=double
|
||||
# Logging modules to check that the string format arguments are in logging
|
||||
# function parameter format
|
||||
logging-modules=logging,absl.logging,tensorflow.io.logging
|
||||
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=4
|
||||
|
||||
# Ignore comments when computing similarities.
|
||||
ignore-comments=yes
|
||||
|
||||
# Ignore docstrings when computing similarities.
|
||||
ignore-docstrings=yes
|
||||
|
||||
# Ignore imports when computing similarities.
|
||||
ignore-imports=no
|
||||
|
||||
|
||||
[SPELLING]
|
||||
|
||||
# Spelling dictionary name. Available dictionaries: none. To make it working
|
||||
# install python-enchant package.
|
||||
spelling-dict=
|
||||
|
||||
# List of comma separated words that should not be checked.
|
||||
spelling-ignore-words=
|
||||
|
||||
# A path to a file that contains private dictionary; one word per line.
|
||||
spelling-private-dict-file=
|
||||
|
||||
# Tells whether to store unknown words to indicated private dictionary in
|
||||
# --spelling-private-dict-file option instead of raising a message.
|
||||
spelling-store-unknown-words=no
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma
|
||||
deprecated-modules=regsub,
|
||||
TERMIOS,
|
||||
Bastion,
|
||||
rexec,
|
||||
sets
|
||||
|
||||
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||
# given file (report RP0402 must not be disabled)
|
||||
import-graph=
|
||||
|
||||
# Create a graph of external dependencies in the given file (report RP0402 must
|
||||
# not be disabled)
|
||||
ext-import-graph=
|
||||
|
||||
# Create a graph of internal dependencies in the given file (report RP0402 must
|
||||
# not be disabled)
|
||||
int-import-graph=
|
||||
|
||||
# Force import order to recognize a module as part of the standard
|
||||
# compatibility libraries.
|
||||
known-standard-library=
|
||||
|
||||
# Force import order to recognize a module as part of a third party library.
|
||||
known-third-party=enchant, absl
|
||||
|
||||
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
||||
# 3 compatible code, which means that the block might have code that exists
|
||||
# only in one or another interpreter, leading to false positives when analysed.
|
||||
analyse-fallback-blocks=no
|
||||
|
||||
|
||||
[CLASSES]
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,
|
||||
__new__,
|
||||
setUp
|
||||
|
||||
# List of member names, which should be excluded from the protected access
|
||||
# warning.
|
||||
exclude-protected=_asdict,
|
||||
_fields,
|
||||
_replace,
|
||||
_source,
|
||||
_make
|
||||
|
||||
# List of valid names for the first argument in a class method.
|
||||
valid-classmethod-first-arg=cls,
|
||||
class_
|
||||
|
||||
# List of valid names for the first argument in a metaclass class method.
|
||||
valid-metaclass-classmethod-first-arg=mcs
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when being caught. Defaults to
|
||||
# "Exception"
|
||||
overgeneral-exceptions=builtins.StandardError,
|
||||
builtins.Exception,
|
||||
builtins.BaseException
|
||||
|
||||
27
CITATION.cff
Normal file
27
CITATION.cff
Normal file
@@ -0,0 +1,27 @@
|
||||
cff-version: 1.2.0
|
||||
message: "If you use this software, please cite it as below."
|
||||
repository-code: "https://github.com/google/OpenSK"
|
||||
license: "Apache-2.0"
|
||||
preferred-citation:
|
||||
type: article
|
||||
authors:
|
||||
- family-names: "Ghinea"
|
||||
given-names: "Diana"
|
||||
- family-names: "Kaczmarczyck"
|
||||
given-names: "Fabian"
|
||||
- family-names: "Pullman"
|
||||
given-names: "Jennifer"
|
||||
- family-names: "Cretin"
|
||||
given-names: "Julien"
|
||||
- family-names: "Kölbl"
|
||||
given-names: "Stefan"
|
||||
- family-names: "Invernizzi"
|
||||
given-names: "Luca"
|
||||
- family-names: "Bursztein"
|
||||
given-names: "Elie"
|
||||
- family-names: "Picod"
|
||||
given-names: "Jean-Michel"
|
||||
title: "Hybrid Post-Quantum Signatures in Hardware Security Keys"
|
||||
journal: "4th ACNS Workshop on Secure Cryptographic Implementation"
|
||||
year: 2023
|
||||
month: 6
|
||||
802
Cargo.lock
generated
802
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
39
Cargo.toml
39
Cargo.toml
@@ -5,36 +5,54 @@ authors = [
|
||||
"Fabian Kaczmarczyck <kaczmarczyck@google.com>",
|
||||
"Guillaume Endignoux <guillaumee@google.com>",
|
||||
"Jean-Michel Picod <jmichel@google.com>",
|
||||
"Julien Cretin <cretin@google.com>",
|
||||
]
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[target.'cfg(any(target_arch = "arm", target_arch = "riscv32"))'.dependencies.libtock_runtime]
|
||||
path = "third_party/libtock-rs/runtime"
|
||||
default-features = false
|
||||
features = ["no_auto_layout", "no_debug_memop"]
|
||||
|
||||
[dependencies]
|
||||
libtock_core = { path = "third_party/libtock-rs/core" }
|
||||
libtock_buttons = { path = "third_party/libtock-rs/apis/buttons" }
|
||||
libtock_platform = { path = "third_party/libtock-rs/platform" }
|
||||
libtock_drivers = { path = "third_party/libtock-drivers" }
|
||||
libtock_alarm = { path = "third_party/libtock-rs/apis/alarm" }
|
||||
libtock_console = { path = "third_party/libtock-rs/apis/console" }
|
||||
libtock_leds = { path = "third_party/libtock-rs/apis/leds" }
|
||||
lang_items = { path = "third_party/lang-items" }
|
||||
cbor = { path = "libraries/cbor" }
|
||||
opensk = { path = "libraries/opensk", default-features = false }
|
||||
sk-cbor = { path = "libraries/cbor" }
|
||||
crypto = { path = "libraries/crypto" }
|
||||
persistent_store = { path = "libraries/persistent_store" }
|
||||
libtock_unittest = { path = "third_party/libtock-rs/unittest", optional = true }
|
||||
byteorder = { version = "1", default-features = false }
|
||||
arrayref = "0.3.6"
|
||||
subtle = { version = "2.2", default-features = false, features = ["nightly"] }
|
||||
rand_core = "0.6.4"
|
||||
ed25519-compact = { version = "1", default-features = false, optional = true }
|
||||
|
||||
[features]
|
||||
config_command = ["opensk/config_command"]
|
||||
debug_allocations = ["lang_items/debug_allocations"]
|
||||
debug_ctap = ["crypto/derive_debug", "libtock_drivers/debug_ctap"]
|
||||
debug_ctap = ["libtock_drivers/debug_ctap", "opensk/debug_ctap"]
|
||||
panic_console = ["lang_items/panic_console"]
|
||||
std = ["cbor/std", "crypto/std", "crypto/derive_debug", "lang_items/std", "persistent_store/std"]
|
||||
std = ["crypto/std", "lang_items/std", "persistent_store/std", "opensk/std", "libtock_unittest"]
|
||||
verbose = ["debug_ctap", "libtock_drivers/verbose_usb"]
|
||||
with_ctap1 = ["crypto/with_ctap1"]
|
||||
with_ctap2_1 = []
|
||||
with_ctap1 = ["opensk/with_ctap1"]
|
||||
with_nfc = ["libtock_drivers/with_nfc"]
|
||||
vendor_hid = ["opensk/vendor_hid"]
|
||||
ed25519 = ["ed25519-compact", "opensk/ed25519"]
|
||||
rust_crypto = ["opensk/rust_crypto"]
|
||||
|
||||
[dev-dependencies]
|
||||
enum-iterator = "0.6.0"
|
||||
|
||||
[build-dependencies]
|
||||
sk-cbor = { path = "libraries/cbor" }
|
||||
uuid = { version = "0.8", features = ["v4"] }
|
||||
openssl = "0.10.55"
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
@@ -43,3 +61,10 @@ lto = true # Link Time Optimization usually reduces size of binaries and static
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
lto = true # Link Time Optimization usually reduces size of binaries and static libraries
|
||||
opt-level = "z"
|
||||
codegen-units = 1
|
||||
|
||||
[profile.release.package]
|
||||
aes = { opt-level = 3 }
|
||||
sha2 = { opt-level = 3 }
|
||||
p256 = { opt-level = 3 }
|
||||
|
||||
77
README.md
77
README.md
@@ -1,42 +1,63 @@
|
||||
# <img alt="OpenSK logo" src="docs/img/OpenSK.svg" width="200px">
|
||||
|
||||
This branch is unmaintained. It implements the CTAP 2.0 version of OpenSK.
|
||||
Please check out the default branch for maintained code.
|
||||
If you are a developer, go to the
|
||||
[develop branch](https://github.com/google/OpenSK/tree/develop).
|
||||

|
||||

|
||||

|
||||

|
||||
[](https://coveralls.io/github/google/OpenSK?branch=develop)
|
||||
|
||||
*News:*
|
||||
|
||||
- 2023-08-24: [PQC paper reference](#Research)
|
||||
|
||||
## OpenSK
|
||||
|
||||
This repository contains a Rust implementation of a
|
||||
[FIDO2](https://fidoalliance.org/fido2/) authenticator.
|
||||
We developed OpenSK as a [Tock OS](https://tockos.org) application.
|
||||
[FIDO2](https://fidoalliance.org/fido2/) security key.
|
||||
Security keys are external devices that can be used for signing in on websites.
|
||||
You can see OpenSK in action in this
|
||||
[video on YouTube](https://www.youtube.com/watch?v=klEozvpw0xg)!
|
||||
|
||||
We intend to bring a full open source experience to security keys, from
|
||||
application to operating system. You can even 3D print your own open source
|
||||
enclosure!
|
||||
You can see OpenSK in action in this
|
||||
[video on YouTube](https://www.youtube.com/watch?v=klEozvpw0xg)!
|
||||
|
||||
<img src="docs/img/enclosure.jpg" alt="OpenSK Enclosure" width="200"/>
|
||||
|
||||
You can run OpenSK as a [Tock OS](https://tockos.org) application, or use the
|
||||
library to bring OpenSK to your own hardware.
|
||||
|
||||
You are viewing the branch for developers. New features are developed here.
|
||||
Go to the default branch for a more stable version of OpenSK.
|
||||
|
||||
### FIDO2
|
||||
|
||||
This branch implements the
|
||||
[CTAP2.0 specification](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html)
|
||||
and is FIDO certified. OpenSK supports U2F, and non-discoverable credentials
|
||||
created with either protocol are compatible with the other.
|
||||
OpenSK's version that implemented CTAP 2.0 was certified by the FIDO Alliance.
|
||||
|
||||
The develop branch tracks the latest release version of the
|
||||
[CTAP specification](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html).
|
||||
This branch is not FIDO certified.
|
||||
OpenSK supports U2F, and non-discoverable credentials created with either
|
||||
protocol are compatible with the other.
|
||||
|
||||
### :warning: Disclaimer
|
||||
|
||||
This project is **proof-of-concept and a research platform**. It is **NOT**
|
||||
meant for a daily usage. The cryptography implementations are not resistent
|
||||
against side-channel attacks.
|
||||
meant for a daily usage. This branch is under development, and therefore less
|
||||
rigorously tested than the numbered branches.
|
||||
|
||||
We're still in the process of integrating the
|
||||
[ARM® CryptoCell-310](https://developer.arm.com/ip-products/security-ip/cryptocell-300-family)
|
||||
embedded in the
|
||||
[Nordic nRF52840 chip](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf52840%2Fcryptocell.html)
|
||||
to enable hardware-accelerated cryptography. Our placeholder implementations of required
|
||||
cryptography algorithms (ECDSA, ECC secp256r1, HMAC-SHA256 and AES256) in Rust are research-quality
|
||||
code. They haven't been reviewed and don't provide constant-time guarantees.
|
||||
to enable hardware-accelerated cryptography.
|
||||
In the meantime, there are 2 options for cryptography implementations:
|
||||
|
||||
* Our own placeholder implementation. The code is research quality and doesn't
|
||||
provide constant-time guarantees.
|
||||
* The [RustCrypto](https://github.com/RustCrypto) interface. Deploy with
|
||||
`--rust-crypto`. Note that our own ECC implementation is faster and has
|
||||
smaller binary size, so not all boards support RustCrypto yet.
|
||||
|
||||
## Hardware
|
||||
|
||||
@@ -66,6 +87,28 @@ Please check our [Troubleshooting and Debugging](docs/debugging.md) section if y
|
||||
have problems with the installation process or during development. To find out what
|
||||
else you can do with your OpenSK, see [Customization](docs/customization.md).
|
||||
|
||||
## Research
|
||||
|
||||
We implemented post-quantum cryptography on OpenSK. The code is released under
|
||||
the [hybrid-pqc tag](https://github.com/google/OpenSK/releases/tag/hybrid-pqc).
|
||||
Our [paper](https://eprint.iacr.org/2022/1225) was published in the ACNS
|
||||
Secure Cryptographic Implementation workshop 2023 and won the best paper award.
|
||||
|
||||
<details>
|
||||
<summary>Bibtex reference</summary>
|
||||
|
||||
```
|
||||
@InProceedings{Ghinea2023hybrid,
|
||||
author= {Diana Ghinea and Fabian Kaczmarczyck and Jennifer Pullman and Julien Cretin and Rafael Misoczki and Stefan Kölbl and Luca Invernizzi and Elie Bursztein and Jean-Michel Picod},
|
||||
title= {{Hybrid Post-Quantum Signatures in Hardware Security Keys}},
|
||||
booktitle= {{4th ACNS Workshop on Secure Cryptographic Implementation, Kyoto, Japan}},
|
||||
month= {June},
|
||||
year= {2023},
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Contributing
|
||||
|
||||
See [Contributing.md](docs/contributing.md).
|
||||
|
||||
@@ -6,7 +6,7 @@ build = "build.rs"
|
||||
edition = "2018"
|
||||
|
||||
[[bin]]
|
||||
path = "../nrf52840_dongle/src/main.rs"
|
||||
path = "../nrf52840_dongle_opensk/src/main.rs"
|
||||
name = "nrf52840_dongle_dfu"
|
||||
|
||||
[dependencies]
|
||||
@@ -16,3 +16,6 @@ capsules = { path = "../../../capsules" }
|
||||
kernel = { path = "../../../kernel" }
|
||||
nrf52840 = { path = "../../../chips/nrf52840" }
|
||||
nrf52_components = { path = "../nrf52_components" }
|
||||
|
||||
[features]
|
||||
vendor_hid = ["capsules/vendor_hid"]
|
||||
|
||||
17
boards/nordic/nrf52840_dongle_opensk/Cargo.toml
Normal file
17
boards/nordic/nrf52840_dongle_opensk/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "nrf52840_dongle_opensk"
|
||||
version = "0.1.0"
|
||||
authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
|
||||
build = "build.rs"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
components = { path = "../../components" }
|
||||
cortexm4 = { path = "../../../arch/cortex-m4" }
|
||||
capsules = { path = "../../../capsules" }
|
||||
kernel = { path = "../../../kernel" }
|
||||
nrf52840 = { path = "../../../chips/nrf52840" }
|
||||
nrf52_components = { path = "../nrf52_components" }
|
||||
|
||||
[features]
|
||||
vendor_hid = ["capsules/vendor_hid"]
|
||||
28
boards/nordic/nrf52840_dongle_opensk/Makefile
Normal file
28
boards/nordic/nrf52840_dongle_opensk/Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
# Makefile for building the tock kernel for the nRF development kit
|
||||
|
||||
TARGET=thumbv7em-none-eabi
|
||||
PLATFORM=nrf52840_dongle_opensk
|
||||
|
||||
include ../../Makefile.common
|
||||
|
||||
TOCKLOADER=tockloader
|
||||
|
||||
# Where in the nrf52 flash to load the kernel with `tockloader`
|
||||
KERNEL_ADDRESS=0x00000
|
||||
|
||||
# Upload programs over uart with tockloader
|
||||
ifdef PORT
|
||||
TOCKLOADER_GENERAL_FLAGS += --port $(PORT)
|
||||
endif
|
||||
|
||||
TOCKLOADER_JTAG_FLAGS = --jlink --board nrf52dk
|
||||
|
||||
# Upload the kernel over JTAG
|
||||
.PHONY: flash
|
||||
flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin
|
||||
$(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) $(TOCKLOADER_JTAG_FLAGS) $<
|
||||
|
||||
# Upload the kernel over serial/bootloader
|
||||
.PHONY: program
|
||||
program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).hex
|
||||
$(error Cannot program nRF52840-Dongle over USB. Use \`make flash\` and JTAG)
|
||||
41
boards/nordic/nrf52840_dongle_opensk/README.md
Normal file
41
boards/nordic/nrf52840_dongle_opensk/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
Platform-Specific Instructions: nRF52840-Dongle
|
||||
===================================
|
||||
|
||||
This is an adapted nrf52840\_dongle made to work with OpenSK.
|
||||
|
||||
The [nRF52840 Dongle](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-Dongle)
|
||||
is a platform based around the nRF52840, an SoC with an ARM Cortex-M4 and a BLE radio.
|
||||
The kit is uses a USB key form factor and includes 1 button, 1 red LED and 1 RGB LED.
|
||||
|
||||
## Getting Started
|
||||
|
||||
To program the nRF52840 Dongle with Tock, you will need a JLink JTAG device and the
|
||||
appropriate cables. An example setup is:
|
||||
|
||||
- [JLink JTAG Device](https://www.digikey.com/product-detail/en/segger-microcontroller-systems/8.08.90-J-LINK-EDU/899-1008-ND/2263130)
|
||||
- [ARM to TagConnect Adapter](https://www.digikey.com/product-detail/en/tag-connect-llc/TC2050-ARM2010/TC2050-ARM2010-ND/3528170)
|
||||
- [10pin TagConnect Cable](https://www.digikey.com/product-detail/en/tag-connect-llc/TC2050-IDC-NL/TC2050-IDC-NL-ND/2605367)
|
||||
|
||||
Then, follow the [Tock Getting Started guide](../../../doc/Getting_Started.md)
|
||||
|
||||
JTAG is the preferred method to program. The development kit has the JTAG pins exposed either
|
||||
through the half-moons pads or, below the PCB, on a Tag-Connect TC2050 connector footprint.
|
||||
You need to [install JTAG software](../../../doc/Getting_Started.md#optional-requirements).
|
||||
|
||||
## Programming the kernel
|
||||
Once you have all software installed, you should be able to simply run
|
||||
make flash in this directory to install a fresh kernel.
|
||||
|
||||
## Programming user-level applications
|
||||
You can program an application via JTAG using `tockloader`:
|
||||
|
||||
```shell
|
||||
$ cd libtock-c/examples/<app>
|
||||
$ make
|
||||
$ tockloader install --jlink --board nrf52dk
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
See the [nrf52dk README](../nrf52dk/README.md) for information about debugging
|
||||
the nRF52840 Dongle.
|
||||
4
boards/nordic/nrf52840_dongle_opensk/build.rs
Normal file
4
boards/nordic/nrf52840_dongle_opensk/build.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=layout.ld");
|
||||
println!("cargo:rerun-if-changed=../../kernel_layout.ld");
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
# J-LINK GDB SERVER initialization
|
||||
#
|
||||
# This connects to a GDB Server listening
|
||||
# for commands on localhost at tcp port 2331
|
||||
target remote localhost:2331
|
||||
monitor speed 30
|
||||
file ../../../../target/thumbv7em-none-eabi/release/nrf52840_dongle
|
||||
monitor reset
|
||||
#
|
||||
# CPU core initialization (to be done by user)
|
||||
#
|
||||
# Set the processor mode
|
||||
# monitor reg cpsr = 0xd3
|
||||
# Set auto JTAG speed
|
||||
monitor speed auto
|
||||
# Setup GDB FOR FASTER DOWNLOADS
|
||||
set remote memory-write-packet-size 1024
|
||||
set remote memory-write-packet-size fixed
|
||||
# tui enable
|
||||
# layout split
|
||||
# layout service_pending_interrupts
|
||||
b reset_handler
|
||||
1
boards/nordic/nrf52840_dongle_opensk/jtag/jdbserver_pca10040.sh
Executable file
1
boards/nordic/nrf52840_dongle_opensk/jtag/jdbserver_pca10040.sh
Executable file
@@ -0,0 +1 @@
|
||||
JLinkGDBServer -device nRF52840_xxAA -speed 1200 -if swd -AutoConnect 1 -port 2331
|
||||
2
boards/nordic/nrf52840_dongle_opensk/layout.ld
Normal file
2
boards/nordic/nrf52840_dongle_opensk/layout.ld
Normal file
@@ -0,0 +1,2 @@
|
||||
INCLUDE ../nrf52840_chip_layout.ld
|
||||
INCLUDE ../../kernel_layout.ld
|
||||
68
boards/nordic/nrf52840_dongle_opensk/src/io.rs
Normal file
68
boards/nordic/nrf52840_dongle_opensk/src/io.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use core::fmt::Write;
|
||||
use core::panic::PanicInfo;
|
||||
use cortexm4;
|
||||
use kernel::debug;
|
||||
use kernel::debug::IoWrite;
|
||||
use kernel::hil::led;
|
||||
use kernel::hil::uart::{self, Configure};
|
||||
use nrf52840::gpio::Pin;
|
||||
|
||||
use crate::{CHIP, PROCESSES, PROCESS_PRINTER};
|
||||
|
||||
struct Writer {
|
||||
initialized: bool,
|
||||
}
|
||||
|
||||
static mut WRITER: Writer = Writer { initialized: false };
|
||||
|
||||
impl Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
|
||||
self.write(s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IoWrite for Writer {
|
||||
fn write(&mut self, buf: &[u8]) {
|
||||
// Here, we create a second instance of the Uarte struct.
|
||||
// This is okay because we only call this during a panic, and
|
||||
// we will never actually process the interrupts
|
||||
let uart = nrf52840::uart::Uarte::new();
|
||||
if !self.initialized {
|
||||
self.initialized = true;
|
||||
let _ = uart.configure(uart::Parameters {
|
||||
baud_rate: 115200,
|
||||
stop_bits: uart::StopBits::One,
|
||||
parity: uart::Parity::None,
|
||||
hw_flow_control: false,
|
||||
width: uart::Width::Eight,
|
||||
});
|
||||
}
|
||||
for &c in buf {
|
||||
unsafe {
|
||||
uart.send_byte(c);
|
||||
}
|
||||
while !uart.tx_ready() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[no_mangle]
|
||||
#[panic_handler]
|
||||
/// Panic handler
|
||||
pub unsafe extern "C" fn panic_fmt(pi: &PanicInfo) -> ! {
|
||||
// The nRF52840 Dongle LEDs (see back of board)
|
||||
let led_kernel_pin = &nrf52840::gpio::GPIOPin::new(Pin::P0_06);
|
||||
let led = &mut led::LedLow::new(led_kernel_pin);
|
||||
let writer = &mut WRITER;
|
||||
debug::panic(
|
||||
&mut [led],
|
||||
writer,
|
||||
pi,
|
||||
&cortexm4::support::nop,
|
||||
&PROCESSES,
|
||||
&CHIP,
|
||||
&PROCESS_PRINTER,
|
||||
)
|
||||
}
|
||||
497
boards/nordic/nrf52840_dongle_opensk/src/main.rs
Normal file
497
boards/nordic/nrf52840_dongle_opensk/src/main.rs
Normal file
@@ -0,0 +1,497 @@
|
||||
//! Tock kernel for the Nordic Semiconductor nRF52840 dongle.
|
||||
//!
|
||||
//! It is based on nRF52840 SoC (Cortex M4 core with a BLE transceiver) with
|
||||
//! many exported I/O and peripherals.
|
||||
|
||||
#![no_std]
|
||||
// Disable this attribute when documenting, as a workaround for
|
||||
// https://github.com/rust-lang/rust/issues/62184.
|
||||
#![cfg_attr(not(doc), no_main)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use capsules::virtual_alarm::VirtualMuxAlarm;
|
||||
use kernel::component::Component;
|
||||
use kernel::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
|
||||
use kernel::hil::led::LedLow;
|
||||
use kernel::hil::time::Counter;
|
||||
use kernel::platform::{KernelResources, SyscallDriverLookup, SyscallFilter};
|
||||
use kernel::scheduler::round_robin::RoundRobinSched;
|
||||
#[allow(unused_imports)]
|
||||
use kernel::{capabilities, create_capability, debug, debug_gpio, debug_verbose, static_init};
|
||||
use kernel::{StorageLocation, StorageType};
|
||||
use nrf52840::gpio::Pin;
|
||||
use nrf52840::interrupt_service::Nrf52840DefaultPeripherals;
|
||||
use nrf52_components::{self, UartChannel, UartPins};
|
||||
|
||||
// The nRF52840 Dongle LEDs
|
||||
const LED1_PIN: Pin = Pin::P0_06;
|
||||
const LED2_R_PIN: Pin = Pin::P0_08;
|
||||
const LED2_G_PIN: Pin = Pin::P1_09;
|
||||
const LED2_B_PIN: Pin = Pin::P0_12;
|
||||
|
||||
// The nRF52840 Dongle button
|
||||
const BUTTON_PIN: Pin = Pin::P1_06;
|
||||
const BUTTON_RST_PIN: Pin = Pin::P0_18;
|
||||
|
||||
const UART_RTS: Option<Pin> = Some(Pin::P0_13);
|
||||
const UART_TXD: Pin = Pin::P0_15;
|
||||
const UART_CTS: Option<Pin> = Some(Pin::P0_17);
|
||||
const UART_RXD: Pin = Pin::P0_20;
|
||||
|
||||
// SPI pins not currently in use, but left here for convenience
|
||||
const _SPI_MOSI: Pin = Pin::P1_01;
|
||||
const _SPI_MISO: Pin = Pin::P1_02;
|
||||
const _SPI_CLK: Pin = Pin::P1_04;
|
||||
|
||||
/// UART Writer
|
||||
pub mod io;
|
||||
|
||||
const VENDOR_ID: u16 = 0x1915; // Nordic Semiconductor
|
||||
const PRODUCT_ID: u16 = 0x521f; // nRF52840 Dongle (PCA10059)
|
||||
static STRINGS: &'static [&'static str] = &[
|
||||
// Manufacturer
|
||||
"Nordic Semiconductor ASA",
|
||||
// Product
|
||||
"OpenSK",
|
||||
// Serial number
|
||||
"v1.0",
|
||||
// Interface description + main HID string
|
||||
"FIDO2",
|
||||
// vendor HID string
|
||||
"Vendor HID",
|
||||
];
|
||||
|
||||
// State for loading and holding applications.
|
||||
// How should the kernel respond when a process faults.
|
||||
const FAULT_RESPONSE: kernel::process::PanicFaultPolicy = kernel::process::PanicFaultPolicy {};
|
||||
|
||||
// Number of concurrent processes this platform supports.
|
||||
const NUM_PROCS: usize = 8;
|
||||
|
||||
static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
|
||||
[None; NUM_PROCS];
|
||||
|
||||
static mut STORAGE_LOCATIONS: [StorageLocation; 2] = [
|
||||
// We implement NUM_PAGES = 20 as 16 + 4 to satisfy the MPU.
|
||||
StorageLocation {
|
||||
address: 0xC0000,
|
||||
size: 0x10000, // 16 pages
|
||||
storage_type: StorageType::Store,
|
||||
},
|
||||
StorageLocation {
|
||||
address: 0xD0000,
|
||||
size: 0x4000, // 4 pages
|
||||
storage_type: StorageType::Store,
|
||||
},
|
||||
];
|
||||
|
||||
// Static reference to chip for panic dumps
|
||||
static mut CHIP: Option<&'static nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>> = None;
|
||||
// Static reference to process printer for panic dumps
|
||||
static mut PROCESS_PRINTER: Option<&'static kernel::process::ProcessPrinterText> = None;
|
||||
|
||||
/// Flash buffer for the custom nvmc driver
|
||||
static mut APP_FLASH_BUFFER: [u8; 0x1000] = [0; 0x1000];
|
||||
|
||||
/// Dummy buffer that causes the linker to reserve enough space for the stack.
|
||||
#[no_mangle]
|
||||
#[link_section = ".stack_buffer"]
|
||||
pub static mut STACK_MEMORY: [u8; 0x1000] = [0; 0x1000];
|
||||
|
||||
/// Supported drivers by the platform
|
||||
pub struct Platform {
|
||||
button: &'static capsules::button::Button<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
pconsole: &'static capsules::process_console::ProcessConsole<
|
||||
'static,
|
||||
VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
|
||||
components::process_console::Capability,
|
||||
>,
|
||||
console: &'static capsules::console::Console<'static>,
|
||||
gpio: &'static capsules::gpio::GPIO<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
led: &'static capsules::led::LedDriver<
|
||||
'static,
|
||||
LedLow<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
4,
|
||||
>,
|
||||
rng: &'static capsules::rng::RngDriver<'static>,
|
||||
ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
|
||||
analog_comparator: &'static capsules::analog_comparator::AnalogComparator<
|
||||
'static,
|
||||
nrf52840::acomp::Comparator<'static>,
|
||||
>,
|
||||
alarm: &'static capsules::alarm::AlarmDriver<
|
||||
'static,
|
||||
capsules::virtual_alarm::VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
|
||||
>,
|
||||
scheduler: &'static RoundRobinSched<'static>,
|
||||
systick: cortexm4::systick::SysTick,
|
||||
nvmc: &'static nrf52840::nvmc::SyscallDriver,
|
||||
usb: &'static capsules::usb::usb_ctap::CtapUsbSyscallDriver<
|
||||
'static,
|
||||
'static,
|
||||
nrf52840::usbd::Usbd<'static>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl SyscallDriverLookup for Platform {
|
||||
fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
|
||||
{
|
||||
match driver_num {
|
||||
capsules::console::DRIVER_NUM => f(Some(self.console)),
|
||||
capsules::gpio::DRIVER_NUM => f(Some(self.gpio)),
|
||||
capsules::alarm::DRIVER_NUM => f(Some(self.alarm)),
|
||||
capsules::led::DRIVER_NUM => f(Some(self.led)),
|
||||
capsules::button::DRIVER_NUM => f(Some(self.button)),
|
||||
capsules::rng::DRIVER_NUM => f(Some(self.rng)),
|
||||
capsules::analog_comparator::DRIVER_NUM => f(Some(self.analog_comparator)),
|
||||
nrf52840::nvmc::DRIVER_NUM => f(Some(self.nvmc)),
|
||||
capsules::usb::usb_ctap::DRIVER_NUM => f(Some(self.usb)),
|
||||
kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
|
||||
_ => f(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SyscallFilter for Platform {
|
||||
fn filter_syscall(
|
||||
&self,
|
||||
process: &dyn kernel::process::Process,
|
||||
syscall: &kernel::syscall::Syscall,
|
||||
) -> Result<(), kernel::errorcode::ErrorCode> {
|
||||
use kernel::syscall::Syscall;
|
||||
match *syscall {
|
||||
Syscall::Command {
|
||||
driver_number: nrf52840::nvmc::DRIVER_NUM,
|
||||
subdriver_number: cmd,
|
||||
arg0: ptr,
|
||||
arg1: len,
|
||||
} if (cmd == 2 || cmd == 3) && !process.fits_in_storage_location(ptr, len) => {
|
||||
Err(kernel::ErrorCode::INVAL)
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KernelResources<nrf52840::chip::NRF52<'static, Nrf52840DefaultPeripherals<'static>>>
|
||||
for Platform
|
||||
{
|
||||
type SyscallDriverLookup = Self;
|
||||
type SyscallFilter = Self;
|
||||
type ProcessFault = ();
|
||||
type Scheduler = RoundRobinSched<'static>;
|
||||
type SchedulerTimer = cortexm4::systick::SysTick;
|
||||
type WatchDog = ();
|
||||
type ContextSwitchCallback = ();
|
||||
|
||||
fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
|
||||
&self
|
||||
}
|
||||
fn syscall_filter(&self) -> &Self::SyscallFilter {
|
||||
&self
|
||||
}
|
||||
fn process_fault(&self) -> &Self::ProcessFault {
|
||||
&()
|
||||
}
|
||||
fn scheduler(&self) -> &Self::Scheduler {
|
||||
self.scheduler
|
||||
}
|
||||
fn scheduler_timer(&self) -> &Self::SchedulerTimer {
|
||||
&self.systick
|
||||
}
|
||||
fn watchdog(&self) -> &Self::WatchDog {
|
||||
&()
|
||||
}
|
||||
fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
|
||||
&()
|
||||
}
|
||||
}
|
||||
|
||||
/// This is in a separate, inline(never) function so that its stack frame is
|
||||
/// removed when this function returns. Otherwise, the stack space used for
|
||||
/// these static_inits is wasted.
|
||||
#[inline(never)]
|
||||
unsafe fn get_peripherals() -> &'static mut Nrf52840DefaultPeripherals<'static> {
|
||||
// Initialize chip peripheral drivers
|
||||
let nrf52840_peripherals = static_init!(
|
||||
Nrf52840DefaultPeripherals,
|
||||
Nrf52840DefaultPeripherals::new()
|
||||
);
|
||||
|
||||
nrf52840_peripherals
|
||||
}
|
||||
|
||||
/// Main function called after RAM initialized.
|
||||
#[no_mangle]
|
||||
pub unsafe fn main() {
|
||||
nrf52840::init();
|
||||
|
||||
let nrf52840_peripherals = get_peripherals();
|
||||
|
||||
// set up circular peripheral dependencies
|
||||
nrf52840_peripherals.init();
|
||||
let base_peripherals = &nrf52840_peripherals.nrf52;
|
||||
|
||||
let board_kernel = static_init!(
|
||||
kernel::Kernel,
|
||||
kernel::Kernel::new_with_storage(&PROCESSES, &STORAGE_LOCATIONS)
|
||||
);
|
||||
|
||||
// GPIOs
|
||||
let gpio = components::gpio::GpioComponent::new(
|
||||
board_kernel,
|
||||
capsules::gpio::DRIVER_NUM,
|
||||
components::gpio_component_helper!(
|
||||
nrf52840::gpio::GPIOPin,
|
||||
// left side of the USB plug
|
||||
0 => &nrf52840_peripherals.gpio_port[Pin::P0_13],
|
||||
1 => &nrf52840_peripherals.gpio_port[Pin::P0_15],
|
||||
2 => &nrf52840_peripherals.gpio_port[Pin::P0_17],
|
||||
3 => &nrf52840_peripherals.gpio_port[Pin::P0_20],
|
||||
4 => &nrf52840_peripherals.gpio_port[Pin::P0_22],
|
||||
5 => &nrf52840_peripherals.gpio_port[Pin::P0_24],
|
||||
6 => &nrf52840_peripherals.gpio_port[Pin::P1_00],
|
||||
7 => &nrf52840_peripherals.gpio_port[Pin::P0_09],
|
||||
8 => &nrf52840_peripherals.gpio_port[Pin::P0_10],
|
||||
// right side of the USB plug
|
||||
9 => &nrf52840_peripherals.gpio_port[Pin::P0_31],
|
||||
10 => &nrf52840_peripherals.gpio_port[Pin::P0_29],
|
||||
11 => &nrf52840_peripherals.gpio_port[Pin::P0_02],
|
||||
12 => &nrf52840_peripherals.gpio_port[Pin::P1_15],
|
||||
13 => &nrf52840_peripherals.gpio_port[Pin::P1_13],
|
||||
14 => &nrf52840_peripherals.gpio_port[Pin::P1_10],
|
||||
// Below the PCB
|
||||
15 => &nrf52840_peripherals.gpio_port[Pin::P0_26],
|
||||
16 => &nrf52840_peripherals.gpio_port[Pin::P0_04],
|
||||
17 => &nrf52840_peripherals.gpio_port[Pin::P0_11],
|
||||
18 => &nrf52840_peripherals.gpio_port[Pin::P0_14],
|
||||
19 => &nrf52840_peripherals.gpio_port[Pin::P1_11],
|
||||
20 => &nrf52840_peripherals.gpio_port[Pin::P1_07],
|
||||
21 => &nrf52840_peripherals.gpio_port[Pin::P1_01],
|
||||
22 => &nrf52840_peripherals.gpio_port[Pin::P1_04],
|
||||
23 => &nrf52840_peripherals.gpio_port[Pin::P1_02]
|
||||
),
|
||||
)
|
||||
.finalize(components::gpio_component_buf!(nrf52840::gpio::GPIOPin));
|
||||
|
||||
let button = components::button::ButtonComponent::new(
|
||||
board_kernel,
|
||||
capsules::button::DRIVER_NUM,
|
||||
components::button_component_helper!(
|
||||
nrf52840::gpio::GPIOPin,
|
||||
(
|
||||
&nrf52840_peripherals.gpio_port[BUTTON_PIN],
|
||||
kernel::hil::gpio::ActivationMode::ActiveLow,
|
||||
kernel::hil::gpio::FloatingState::PullUp
|
||||
)
|
||||
),
|
||||
)
|
||||
.finalize(components::button_component_buf!(nrf52840::gpio::GPIOPin));
|
||||
|
||||
let led = components::led::LedsComponent::new().finalize(components::led_component_helper!(
|
||||
LedLow<'static, nrf52840::gpio::GPIOPin>,
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED2_R_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED2_G_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED2_B_PIN]),
|
||||
));
|
||||
|
||||
let chip = static_init!(
|
||||
nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>,
|
||||
nrf52840::chip::NRF52::new(nrf52840_peripherals)
|
||||
);
|
||||
CHIP = Some(chip);
|
||||
|
||||
nrf52_components::startup::NrfStartupComponent::new(
|
||||
false,
|
||||
BUTTON_RST_PIN,
|
||||
nrf52840::uicr::Regulator0Output::V3_0,
|
||||
&base_peripherals.nvmc,
|
||||
)
|
||||
.finalize(());
|
||||
|
||||
// Create capabilities that the board needs to call certain protected kernel
|
||||
// functions.
|
||||
let process_management_capability =
|
||||
create_capability!(capabilities::ProcessManagementCapability);
|
||||
let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
|
||||
let memory_allocation_capability = create_capability!(capabilities::MemoryAllocationCapability);
|
||||
|
||||
let gpio_port = &nrf52840_peripherals.gpio_port;
|
||||
|
||||
// Configure kernel debug gpios as early as possible
|
||||
kernel::debug::assign_gpios(
|
||||
Some(&gpio_port[LED2_R_PIN]),
|
||||
Some(&gpio_port[LED2_G_PIN]),
|
||||
Some(&gpio_port[LED2_B_PIN]),
|
||||
);
|
||||
|
||||
let rtc = &base_peripherals.rtc;
|
||||
let _ = rtc.start();
|
||||
let mux_alarm = components::alarm::AlarmMuxComponent::new(rtc)
|
||||
.finalize(components::alarm_mux_component_helper!(nrf52840::rtc::Rtc));
|
||||
let alarm = components::alarm::AlarmDriverComponent::new(
|
||||
board_kernel,
|
||||
capsules::alarm::DRIVER_NUM,
|
||||
mux_alarm,
|
||||
)
|
||||
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
|
||||
let uart_channel = UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD));
|
||||
let channel = nrf52_components::UartChannelComponent::new(
|
||||
uart_channel,
|
||||
mux_alarm,
|
||||
&base_peripherals.uarte0,
|
||||
)
|
||||
.finalize(());
|
||||
|
||||
let dynamic_deferred_call_clients =
|
||||
static_init!([DynamicDeferredCallClientState; 3], Default::default());
|
||||
let dynamic_deferred_caller = static_init!(
|
||||
DynamicDeferredCall,
|
||||
DynamicDeferredCall::new(dynamic_deferred_call_clients)
|
||||
);
|
||||
DynamicDeferredCall::set_global_instance(dynamic_deferred_caller);
|
||||
|
||||
let process_printer =
|
||||
components::process_printer::ProcessPrinterTextComponent::new().finalize(());
|
||||
PROCESS_PRINTER = Some(process_printer);
|
||||
|
||||
// Create a shared UART channel for the console and for kernel debug.
|
||||
let uart_mux =
|
||||
components::console::UartMuxComponent::new(channel, 115200, dynamic_deferred_caller)
|
||||
.finalize(());
|
||||
|
||||
let pconsole = components::process_console::ProcessConsoleComponent::new(
|
||||
board_kernel,
|
||||
uart_mux,
|
||||
mux_alarm,
|
||||
process_printer,
|
||||
)
|
||||
.finalize(components::process_console_component_helper!(
|
||||
nrf52840::rtc::Rtc<'static>
|
||||
));
|
||||
|
||||
// Setup the console.
|
||||
let console = components::console::ConsoleComponent::new(
|
||||
board_kernel,
|
||||
capsules::console::DRIVER_NUM,
|
||||
uart_mux,
|
||||
)
|
||||
.finalize(components::console_component_helper!());
|
||||
// Create the debugger object that handles calls to `debug!()`.
|
||||
components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(());
|
||||
|
||||
let rng = components::rng::RngComponent::new(
|
||||
board_kernel,
|
||||
capsules::rng::DRIVER_NUM,
|
||||
&base_peripherals.trng,
|
||||
)
|
||||
.finalize(());
|
||||
|
||||
// Initialize AC using AIN5 (P0.29) as VIN+ and VIN- as AIN0 (P0.02)
|
||||
// These are hardcoded pin assignments specified in the driver
|
||||
let analog_comparator = components::analog_comparator::AcComponent::new(
|
||||
&base_peripherals.acomp,
|
||||
components::acomp_component_helper!(
|
||||
nrf52840::acomp::Channel,
|
||||
&nrf52840::acomp::CHANNEL_AC0
|
||||
),
|
||||
board_kernel,
|
||||
capsules::analog_comparator::DRIVER_NUM,
|
||||
)
|
||||
.finalize(components::acomp_component_buf!(
|
||||
nrf52840::acomp::Comparator
|
||||
));
|
||||
|
||||
let nvmc = static_init!(
|
||||
nrf52840::nvmc::SyscallDriver,
|
||||
nrf52840::nvmc::SyscallDriver::new(
|
||||
&base_peripherals.nvmc,
|
||||
board_kernel.create_grant(nrf52840::nvmc::DRIVER_NUM, &memory_allocation_capability),
|
||||
dynamic_deferred_caller,
|
||||
&mut APP_FLASH_BUFFER,
|
||||
)
|
||||
);
|
||||
nvmc.set_deferred_handle(
|
||||
dynamic_deferred_caller
|
||||
.register(nvmc)
|
||||
.expect("no deferred call slot available for nvmc"),
|
||||
);
|
||||
|
||||
// Configure USB controller
|
||||
let usb = components::usb_ctap::UsbCtapComponent::new(
|
||||
board_kernel,
|
||||
capsules::usb::usb_ctap::DRIVER_NUM,
|
||||
&nrf52840_peripherals.usbd,
|
||||
capsules::usb::usbc_client::MAX_CTRL_PACKET_SIZE_NRF52840,
|
||||
VENDOR_ID,
|
||||
PRODUCT_ID,
|
||||
STRINGS,
|
||||
)
|
||||
.finalize(components::usb_ctap_component_helper!(nrf52840::usbd::Usbd));
|
||||
|
||||
nrf52_components::NrfClockComponent::new(&base_peripherals.clock).finalize(());
|
||||
|
||||
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
|
||||
.finalize(components::rr_component_helper!(NUM_PROCS));
|
||||
|
||||
let platform = Platform {
|
||||
button,
|
||||
pconsole,
|
||||
console,
|
||||
led,
|
||||
gpio,
|
||||
rng,
|
||||
alarm,
|
||||
analog_comparator,
|
||||
nvmc,
|
||||
usb,
|
||||
ipc: kernel::ipc::IPC::new(
|
||||
board_kernel,
|
||||
kernel::ipc::DRIVER_NUM,
|
||||
&memory_allocation_capability,
|
||||
),
|
||||
scheduler,
|
||||
systick: cortexm4::systick::SysTick::new_with_calibration(6400_0000),
|
||||
};
|
||||
|
||||
let _ = platform.pconsole.start();
|
||||
debug!("Initialization complete. Entering main loop\r");
|
||||
debug!("{}", &nrf52840::ficr::FICR_INSTANCE);
|
||||
|
||||
// These symbols are defined in the linker script.
|
||||
extern "C" {
|
||||
/// Beginning of the ROM region containing app images.
|
||||
static _sapps: u8;
|
||||
/// End of the ROM region containing app images.
|
||||
static _eapps: u8;
|
||||
/// Beginning of the RAM region for app memory.
|
||||
static mut _sappmem: u8;
|
||||
/// End of the RAM region for app memory.
|
||||
static _eappmem: u8;
|
||||
}
|
||||
|
||||
kernel::process::load_processes(
|
||||
board_kernel,
|
||||
chip,
|
||||
core::slice::from_raw_parts(
|
||||
&_sapps as *const u8,
|
||||
&_eapps as *const u8 as usize - &_sapps as *const u8 as usize,
|
||||
),
|
||||
core::slice::from_raw_parts_mut(
|
||||
&mut _sappmem as *mut u8,
|
||||
&_eappmem as *const u8 as usize - &_sappmem as *const u8 as usize,
|
||||
),
|
||||
&mut PROCESSES,
|
||||
&FAULT_RESPONSE,
|
||||
&process_management_capability,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
debug!("Error loading processes!");
|
||||
debug!("{:?}", err);
|
||||
});
|
||||
|
||||
board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
|
||||
}
|
||||
@@ -12,3 +12,6 @@ capsules = { path = "../../../capsules" }
|
||||
kernel = { path = "../../../kernel" }
|
||||
nrf52840 = { path = "../../../chips/nrf52840" }
|
||||
nrf52_components = { path = "../nrf52_components" }
|
||||
|
||||
[features]
|
||||
vendor_hid = ["capsules/vendor_hid"]
|
||||
|
||||
@@ -7,8 +7,7 @@ use kernel::hil::led;
|
||||
use kernel::hil::uart::{self, Configure};
|
||||
use nrf52840::gpio::Pin;
|
||||
|
||||
use crate::CHIP;
|
||||
use crate::PROCESSES;
|
||||
use crate::{CHIP, PROCESSES, PROCESS_PRINTER};
|
||||
|
||||
struct Writer {
|
||||
initialized: bool,
|
||||
@@ -25,10 +24,13 @@ impl Write for Writer {
|
||||
|
||||
impl IoWrite for Writer {
|
||||
fn write(&mut self, buf: &[u8]) {
|
||||
let uart = unsafe { &mut nrf52840::uart::UARTE0 };
|
||||
// Here, we create a second instance of the Uarte struct.
|
||||
// This is okay because we only call this during a panic, and
|
||||
// we will never actually process the interrupts
|
||||
let uart = nrf52840::uart::Uarte::new();
|
||||
if !self.initialized {
|
||||
self.initialized = true;
|
||||
uart.configure(uart::Parameters {
|
||||
let _ = uart.configure(uart::Parameters {
|
||||
baud_rate: 115200,
|
||||
stop_bits: uart::StopBits::One,
|
||||
parity: uart::Parity::None,
|
||||
@@ -51,8 +53,8 @@ impl IoWrite for Writer {
|
||||
/// Panic handler
|
||||
pub unsafe extern "C" fn panic_fmt(pi: &PanicInfo) -> ! {
|
||||
// The nRF52840 Dongle LEDs (see back of board)
|
||||
const LED1_PIN: Pin = Pin::P0_23;
|
||||
let led = &mut led::LedLow::new(&mut nrf52840::gpio::PORT[LED1_PIN]);
|
||||
let led_kernel_pin = &nrf52840::gpio::GPIOPin::new(Pin::P0_23);
|
||||
let led = &mut led::LedLow::new(led_kernel_pin);
|
||||
let writer = &mut WRITER;
|
||||
debug::panic(
|
||||
&mut [led],
|
||||
@@ -61,5 +63,6 @@ pub unsafe extern "C" fn panic_fmt(pi: &PanicInfo) -> ! {
|
||||
&cortexm4::support::nop,
|
||||
&PROCESSES,
|
||||
&CHIP,
|
||||
&PROCESS_PRINTER,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,16 +7,19 @@
|
||||
// Disable this attribute when documenting, as a workaround for
|
||||
// https://github.com/rust-lang/rust/issues/62184.
|
||||
#![cfg_attr(not(doc), no_main)]
|
||||
#![feature(const_in_array_repeat_expressions)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use capsules::virtual_alarm::VirtualMuxAlarm;
|
||||
use kernel::common::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
|
||||
use kernel::component::Component;
|
||||
use kernel::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
|
||||
use kernel::hil::led::LedLow;
|
||||
use kernel::hil::time::Counter;
|
||||
use kernel::platform::{KernelResources, SyscallDriverLookup, SyscallFilter};
|
||||
use kernel::scheduler::round_robin::RoundRobinSched;
|
||||
#[allow(unused_imports)]
|
||||
use kernel::{capabilities, create_capability, debug, debug_gpio, debug_verbose, static_init};
|
||||
use kernel::hil::usb::UsbController;
|
||||
use nrf52840::gpio::Pin;
|
||||
use nrf52840::interrupt_service::Nrf52840DefaultPeripherals;
|
||||
use nrf52_components::{self, UartChannel, UartPins};
|
||||
|
||||
// The nRF52840 MDK USB Dongle LEDs
|
||||
@@ -26,17 +29,13 @@ const LED1_B_PIN: Pin = Pin::P0_24;
|
||||
|
||||
// The nRF52840 Dongle button
|
||||
const BUTTON_PIN: Pin = Pin::P0_18;
|
||||
const BUTTON_RST_PIN: Pin = Pin::P0_02;
|
||||
const _BUTTON_RST_PIN: Pin = Pin::P0_02;
|
||||
|
||||
const UART_RTS: Option<Pin> = Some(Pin::P0_21);
|
||||
const UART_TXD: Pin = Pin::P0_20;
|
||||
const UART_CTS: Option<Pin> = Some(Pin::P0_03);
|
||||
const UART_RXD: Pin = Pin::P0_19;
|
||||
|
||||
// Constants related to the configuration of the 15.4 network stack
|
||||
const SRC_MAC: u16 = 0xf00f;
|
||||
const PAN_ID: u16 = 0xABCD;
|
||||
|
||||
/// UART Writer
|
||||
pub mod io;
|
||||
|
||||
@@ -49,25 +48,41 @@ static STRINGS: &'static [&'static str] = &[
|
||||
"OpenSK",
|
||||
// Serial number
|
||||
"v1.0",
|
||||
// Interface description + main HID string
|
||||
"FIDO2",
|
||||
// vendor HID string
|
||||
"Vendor HID",
|
||||
];
|
||||
|
||||
// State for loading and holding applications.
|
||||
// How should the kernel respond when a process faults.
|
||||
const FAULT_RESPONSE: kernel::procs::FaultResponse = kernel::procs::FaultResponse::Panic;
|
||||
const FAULT_RESPONSE: kernel::process::PanicFaultPolicy = kernel::process::PanicFaultPolicy {};
|
||||
|
||||
// Number of concurrent processes this platform supports.
|
||||
const NUM_PROCS: usize = 8;
|
||||
|
||||
static mut PROCESSES: [Option<&'static dyn kernel::procs::ProcessType>; NUM_PROCS] =
|
||||
static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
|
||||
[None; NUM_PROCS];
|
||||
|
||||
static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 1] = [kernel::StorageLocation {
|
||||
address: 0xC0000,
|
||||
size: 0x40000,
|
||||
}];
|
||||
static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 2] = [
|
||||
// We implement NUM_PAGES = 20 as 16 + 4 to satisfy the MPU.
|
||||
kernel::StorageLocation {
|
||||
address: 0xC0000,
|
||||
size: 0x10000, // 16 pages
|
||||
storage_type: kernel::StorageType::Store,
|
||||
},
|
||||
kernel::StorageLocation {
|
||||
address: 0xD0000,
|
||||
size: 0x4000, // 4 pages
|
||||
storage_type: kernel::StorageType::Store,
|
||||
},
|
||||
];
|
||||
|
||||
// Static reference to chip for panic dumps
|
||||
static mut CHIP: Option<&'static nrf52840::chip::Chip> = None;
|
||||
static mut CHIP: Option<&'static nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>> = None;
|
||||
static mut PROCESS_PRINTER: Option<&'static kernel::process::ProcessPrinterText> = None;
|
||||
/// Flash buffer for the custom nvmc driver
|
||||
static mut APP_FLASH_BUFFER: [u8; 0x1000] = [0; 0x1000];
|
||||
|
||||
/// Dummy buffer that causes the linker to reserve enough space for the stack.
|
||||
#[no_mangle]
|
||||
@@ -76,23 +91,21 @@ pub static mut STACK_MEMORY: [u8; 0x1000] = [0; 0x1000];
|
||||
|
||||
/// Supported drivers by the platform
|
||||
pub struct Platform {
|
||||
ble_radio: &'static capsules::ble_advertising_driver::BLE<
|
||||
'static,
|
||||
nrf52840::ble_radio::Radio<'static>,
|
||||
VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
|
||||
>,
|
||||
ieee802154_radio: &'static capsules::ieee802154::RadioDriver<'static>,
|
||||
button: &'static capsules::button::Button<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
pconsole: &'static capsules::process_console::ProcessConsole<
|
||||
'static,
|
||||
VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
|
||||
components::process_console::Capability,
|
||||
>,
|
||||
console: &'static capsules::console::Console<'static>,
|
||||
gpio: &'static capsules::gpio::GPIO<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
led: &'static capsules::led::LED<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
led: &'static capsules::led::LedDriver<
|
||||
'static,
|
||||
kernel::hil::led::LedLow<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
3,
|
||||
>,
|
||||
rng: &'static capsules::rng::RngDriver<'static>,
|
||||
temp: &'static capsules::temperature::TemperatureSensor<'static>,
|
||||
ipc: kernel::ipc::IPC,
|
||||
ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
|
||||
analog_comparator: &'static capsules::analog_comparator::AnalogComparator<
|
||||
'static,
|
||||
nrf52840::acomp::Comparator<'static>,
|
||||
@@ -101,6 +114,8 @@ pub struct Platform {
|
||||
'static,
|
||||
capsules::virtual_alarm::VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
|
||||
>,
|
||||
scheduler: &'static RoundRobinSched<'static>,
|
||||
systick: cortexm4::systick::SysTick,
|
||||
nvmc: &'static nrf52840::nvmc::SyscallDriver,
|
||||
usb: &'static capsules::usb::usb_ctap::CtapUsbSyscallDriver<
|
||||
'static,
|
||||
@@ -109,10 +124,10 @@ pub struct Platform {
|
||||
>,
|
||||
}
|
||||
|
||||
impl kernel::Platform for Platform {
|
||||
impl SyscallDriverLookup for Platform {
|
||||
fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(Option<&dyn kernel::Driver>) -> R,
|
||||
F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
|
||||
{
|
||||
match driver_num {
|
||||
capsules::console::DRIVER_NUM => f(Some(self.console)),
|
||||
@@ -121,9 +136,6 @@ impl kernel::Platform for Platform {
|
||||
capsules::led::DRIVER_NUM => f(Some(self.led)),
|
||||
capsules::button::DRIVER_NUM => f(Some(self.button)),
|
||||
capsules::rng::DRIVER_NUM => f(Some(self.rng)),
|
||||
capsules::ble_advertising_driver::DRIVER_NUM => f(Some(self.ble_radio)),
|
||||
capsules::ieee802154::DRIVER_NUM => f(Some(self.ieee802154_radio)),
|
||||
capsules::temperature::DRIVER_NUM => f(Some(self.temp)),
|
||||
capsules::analog_comparator::DRIVER_NUM => f(Some(self.analog_comparator)),
|
||||
nrf52840::nvmc::DRIVER_NUM => f(Some(self.nvmc)),
|
||||
capsules::usb::usb_ctap::DRIVER_NUM => f(Some(self.usb)),
|
||||
@@ -131,33 +143,87 @@ impl kernel::Platform for Platform {
|
||||
_ => f(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SyscallFilter for Platform {
|
||||
fn filter_syscall(
|
||||
&self,
|
||||
process: &dyn kernel::procs::ProcessType,
|
||||
process: &dyn kernel::process::Process,
|
||||
syscall: &kernel::syscall::Syscall,
|
||||
) -> Result<(), kernel::ReturnCode> {
|
||||
) -> Result<(), kernel::errorcode::ErrorCode> {
|
||||
use kernel::syscall::Syscall;
|
||||
match *syscall {
|
||||
Syscall::COMMAND {
|
||||
Syscall::Command {
|
||||
driver_number: nrf52840::nvmc::DRIVER_NUM,
|
||||
subdriver_number: cmd,
|
||||
arg0: ptr,
|
||||
arg1: len,
|
||||
} if (cmd == 2 || cmd == 3) && !process.fits_in_storage_location(ptr, len) => {
|
||||
Err(kernel::ReturnCode::EINVAL)
|
||||
Err(kernel::ErrorCode::INVAL)
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Entry point in the vector table called on hard reset.
|
||||
/// This is in a separate, inline(never) function so that its stack frame is
|
||||
/// removed when this function returns. Otherwise, the stack space used for
|
||||
/// these static_inits is wasted.
|
||||
#[inline(never)]
|
||||
unsafe fn get_peripherals() -> &'static mut Nrf52840DefaultPeripherals<'static> {
|
||||
// Initialize chip peripheral drivers
|
||||
let nrf52840_peripherals = static_init!(
|
||||
Nrf52840DefaultPeripherals,
|
||||
Nrf52840DefaultPeripherals::new()
|
||||
);
|
||||
nrf52840_peripherals
|
||||
}
|
||||
impl KernelResources<nrf52840::chip::NRF52<'static, Nrf52840DefaultPeripherals<'static>>>
|
||||
for Platform
|
||||
{
|
||||
type SyscallDriverLookup = Self;
|
||||
type SyscallFilter = Self;
|
||||
type ProcessFault = ();
|
||||
type Scheduler = RoundRobinSched<'static>;
|
||||
type SchedulerTimer = cortexm4::systick::SysTick;
|
||||
type WatchDog = ();
|
||||
type ContextSwitchCallback = ();
|
||||
fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
|
||||
&self
|
||||
}
|
||||
fn syscall_filter(&self) -> &Self::SyscallFilter {
|
||||
&self
|
||||
}
|
||||
fn process_fault(&self) -> &Self::ProcessFault {
|
||||
&()
|
||||
}
|
||||
fn scheduler(&self) -> &Self::Scheduler {
|
||||
self.scheduler
|
||||
}
|
||||
fn scheduler_timer(&self) -> &Self::SchedulerTimer {
|
||||
&self.systick
|
||||
}
|
||||
fn watchdog(&self) -> &Self::WatchDog {
|
||||
&()
|
||||
}
|
||||
fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
|
||||
&()
|
||||
}
|
||||
}
|
||||
|
||||
/// Main function called after RAM initialized.
|
||||
#[no_mangle]
|
||||
pub unsafe fn reset_handler() {
|
||||
pub unsafe fn main() {
|
||||
// Loads relocations and clears BSS
|
||||
nrf52840::init();
|
||||
|
||||
// Initialize chip peripheral drivers
|
||||
let nrf52840_peripherals = get_peripherals();
|
||||
|
||||
// set up circular peripheral dependencies
|
||||
nrf52840_peripherals.init();
|
||||
let base_peripherals = &nrf52840_peripherals.nrf52;
|
||||
|
||||
let board_kernel = static_init!(
|
||||
kernel::Kernel,
|
||||
kernel::Kernel::new_with_storage(&PROCESSES, &STORAGE_LOCATIONS)
|
||||
@@ -165,23 +231,26 @@ pub unsafe fn reset_handler() {
|
||||
// GPIOs
|
||||
let gpio = components::gpio::GpioComponent::new(
|
||||
board_kernel,
|
||||
capsules::gpio::DRIVER_NUM,
|
||||
components::gpio_component_helper!(
|
||||
nrf52840::gpio::GPIOPin,
|
||||
// left side of the USB plug. Right side is used for UART
|
||||
0 => &nrf52840::gpio::PORT[Pin::P0_04],
|
||||
1 => &nrf52840::gpio::PORT[Pin::P0_05],
|
||||
2 => &nrf52840::gpio::PORT[Pin::P0_06],
|
||||
3 => &nrf52840::gpio::PORT[Pin::P0_07],
|
||||
4 => &nrf52840::gpio::PORT[Pin::P0_08]
|
||||
0 => &nrf52840_peripherals.gpio_port[Pin::P0_04],
|
||||
1 => &nrf52840_peripherals.gpio_port[Pin::P0_05],
|
||||
2 => &nrf52840_peripherals.gpio_port[Pin::P0_06],
|
||||
3 => &nrf52840_peripherals.gpio_port[Pin::P0_07],
|
||||
4 => &nrf52840_peripherals.gpio_port[Pin::P0_08]
|
||||
),
|
||||
).finalize(components::gpio_component_buf!(nrf52840::gpio::GPIOPin));
|
||||
)
|
||||
.finalize(components::gpio_component_buf!(nrf52840::gpio::GPIOPin));
|
||||
|
||||
let button = components::button::ButtonComponent::new(
|
||||
board_kernel,
|
||||
capsules::button::DRIVER_NUM,
|
||||
components::button_component_helper!(
|
||||
nrf52840::gpio::GPIOPin,
|
||||
(
|
||||
&nrf52840::gpio::PORT[BUTTON_PIN],
|
||||
&nrf52840_peripherals.gpio_port[BUTTON_PIN],
|
||||
kernel::hil::gpio::ActivationMode::ActiveLow,
|
||||
kernel::hil::gpio::FloatingState::PullUp
|
||||
)
|
||||
@@ -189,33 +258,19 @@ pub unsafe fn reset_handler() {
|
||||
)
|
||||
.finalize(components::button_component_buf!(nrf52840::gpio::GPIOPin));
|
||||
|
||||
let led = components::led::LedsComponent::new(components::led_component_helper!(
|
||||
nrf52840::gpio::GPIOPin,
|
||||
(
|
||||
&nrf52840::gpio::PORT[LED1_R_PIN],
|
||||
kernel::hil::gpio::ActivationMode::ActiveLow
|
||||
),
|
||||
(
|
||||
&nrf52840::gpio::PORT[LED1_G_PIN],
|
||||
kernel::hil::gpio::ActivationMode::ActiveLow
|
||||
),
|
||||
(
|
||||
&nrf52840::gpio::PORT[LED1_B_PIN],
|
||||
kernel::hil::gpio::ActivationMode::ActiveLow
|
||||
)
|
||||
))
|
||||
.finalize(components::led_component_buf!(nrf52840::gpio::GPIOPin));
|
||||
let led = components::led::LedsComponent::new().finalize(components::led_component_helper!(
|
||||
LedLow<'static, nrf52840::gpio::GPIOPin>,
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_R_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_G_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_B_PIN]),
|
||||
));
|
||||
|
||||
let chip = static_init!(nrf52840::chip::Chip, nrf52840::chip::new());
|
||||
let chip = static_init!(
|
||||
nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>,
|
||||
nrf52840::chip::NRF52::new(nrf52840_peripherals)
|
||||
);
|
||||
CHIP = Some(chip);
|
||||
|
||||
nrf52_components::startup::NrfStartupComponent::new(
|
||||
false,
|
||||
BUTTON_RST_PIN,
|
||||
nrf52840::uicr::Regulator0Output::V3_0,
|
||||
)
|
||||
.finalize(());
|
||||
|
||||
// Create capabilities that the board needs to call certain protected kernel
|
||||
// functions.
|
||||
let process_management_capability =
|
||||
@@ -223,7 +278,7 @@ pub unsafe fn reset_handler() {
|
||||
let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
|
||||
let memory_allocation_capability = create_capability!(capabilities::MemoryAllocationCapability);
|
||||
|
||||
let gpio_port = &nrf52840::gpio::PORT;
|
||||
let gpio_port = &nrf52840_peripherals.gpio_port;
|
||||
|
||||
// Configure kernel debug gpios as early as possible
|
||||
kernel::debug::assign_gpios(
|
||||
@@ -232,14 +287,23 @@ pub unsafe fn reset_handler() {
|
||||
Some(&gpio_port[LED1_B_PIN]),
|
||||
);
|
||||
|
||||
let rtc = &nrf52840::rtc::RTC;
|
||||
rtc.start();
|
||||
let rtc = &base_peripherals.rtc;
|
||||
let _ = rtc.start();
|
||||
let mux_alarm = components::alarm::AlarmMuxComponent::new(rtc)
|
||||
.finalize(components::alarm_mux_component_helper!(nrf52840::rtc::Rtc));
|
||||
let alarm = components::alarm::AlarmDriverComponent::new(board_kernel, mux_alarm)
|
||||
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
|
||||
let alarm = components::alarm::AlarmDriverComponent::new(
|
||||
board_kernel,
|
||||
capsules::alarm::DRIVER_NUM,
|
||||
mux_alarm,
|
||||
)
|
||||
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
|
||||
let uart_channel = UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD));
|
||||
let channel = nrf52_components::UartChannelComponent::new(uart_channel, mux_alarm).finalize(());
|
||||
let channel = nrf52_components::UartChannelComponent::new(
|
||||
uart_channel,
|
||||
mux_alarm,
|
||||
&base_peripherals.uarte0,
|
||||
)
|
||||
.finalize(());
|
||||
|
||||
let dynamic_deferred_call_clients =
|
||||
static_init!([DynamicDeferredCallClientState; 2], Default::default());
|
||||
@@ -248,53 +312,51 @@ pub unsafe fn reset_handler() {
|
||||
DynamicDeferredCall::new(dynamic_deferred_call_clients)
|
||||
);
|
||||
DynamicDeferredCall::set_global_instance(dynamic_deferred_caller);
|
||||
let process_printer =
|
||||
components::process_printer::ProcessPrinterTextComponent::new().finalize(());
|
||||
PROCESS_PRINTER = Some(process_printer);
|
||||
|
||||
// Create a shared UART channel for the console and for kernel debug.
|
||||
let uart_mux =
|
||||
components::console::UartMuxComponent::new(channel, 115200, dynamic_deferred_caller)
|
||||
.finalize(());
|
||||
|
||||
let pconsole =
|
||||
components::process_console::ProcessConsoleComponent::new(board_kernel, uart_mux)
|
||||
.finalize(());
|
||||
|
||||
// Setup the console.
|
||||
let console = components::console::ConsoleComponent::new(board_kernel, uart_mux).finalize(());
|
||||
// Create the debugger object that handles calls to `debug!()`.
|
||||
components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(());
|
||||
|
||||
let ble_radio =
|
||||
nrf52_components::BLEComponent::new(board_kernel, &nrf52840::ble_radio::RADIO, mux_alarm)
|
||||
.finalize(());
|
||||
|
||||
let (ieee802154_radio, _mux_mac) = components::ieee802154::Ieee802154Component::new(
|
||||
let pconsole = components::process_console::ProcessConsoleComponent::new(
|
||||
board_kernel,
|
||||
&nrf52840::ieee802154_radio::RADIO,
|
||||
&nrf52840::aes::AESECB,
|
||||
PAN_ID,
|
||||
SRC_MAC,
|
||||
uart_mux,
|
||||
mux_alarm,
|
||||
process_printer,
|
||||
)
|
||||
.finalize(components::ieee802154_component_helper!(
|
||||
nrf52840::ieee802154_radio::Radio,
|
||||
nrf52840::aes::AesECB<'static>
|
||||
.finalize(components::process_console_component_helper!(
|
||||
nrf52840::rtc::Rtc<'static>
|
||||
));
|
||||
|
||||
let temp = components::temperature::TemperatureComponent::new(
|
||||
// Setup the console.
|
||||
let console = components::console::ConsoleComponent::new(
|
||||
board_kernel,
|
||||
&nrf52840::temperature::TEMP,
|
||||
capsules::console::DRIVER_NUM,
|
||||
uart_mux,
|
||||
)
|
||||
.finalize(components::console_component_helper!());
|
||||
// Create the debugger object that handles calls to `debug!()`.
|
||||
components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(());
|
||||
let rng = components::rng::RngComponent::new(
|
||||
board_kernel,
|
||||
capsules::rng::DRIVER_NUM,
|
||||
&base_peripherals.trng,
|
||||
)
|
||||
.finalize(());
|
||||
|
||||
let rng = components::rng::RngComponent::new(board_kernel, &nrf52840::trng::TRNG).finalize(());
|
||||
|
||||
// Initialize AC using AIN5 (P0.29) as VIN+ and VIN- as AIN0 (P0.02)
|
||||
// These are hardcoded pin assignments specified in the driver
|
||||
let analog_comparator = components::analog_comparator::AcComponent::new(
|
||||
&nrf52840::acomp::ACOMP,
|
||||
&base_peripherals.acomp,
|
||||
components::acomp_component_helper!(
|
||||
nrf52840::acomp::Channel,
|
||||
&nrf52840::acomp::CHANNEL_AC0
|
||||
),
|
||||
board_kernel,
|
||||
capsules::analog_comparator::DRIVER_NUM,
|
||||
)
|
||||
.finalize(components::acomp_component_buf!(
|
||||
nrf52840::acomp::Comparator
|
||||
@@ -303,78 +365,60 @@ pub unsafe fn reset_handler() {
|
||||
let nvmc = static_init!(
|
||||
nrf52840::nvmc::SyscallDriver,
|
||||
nrf52840::nvmc::SyscallDriver::new(
|
||||
&nrf52840::nvmc::NVMC,
|
||||
board_kernel.create_grant(&memory_allocation_capability),
|
||||
&base_peripherals.nvmc,
|
||||
board_kernel.create_grant(nrf52840::nvmc::DRIVER_NUM, &memory_allocation_capability),
|
||||
dynamic_deferred_caller,
|
||||
&mut APP_FLASH_BUFFER,
|
||||
)
|
||||
);
|
||||
nvmc.set_deferred_handle(
|
||||
dynamic_deferred_caller
|
||||
.register(nvmc)
|
||||
.expect("no deferred call slot available for nvmc"),
|
||||
);
|
||||
|
||||
// Configure USB controller
|
||||
let usb:
|
||||
&'static capsules::usb::usb_ctap::CtapUsbSyscallDriver<
|
||||
'static,
|
||||
'static,
|
||||
nrf52840::usbd::Usbd<'static>,
|
||||
> = {
|
||||
let usb_ctap = static_init!(
|
||||
capsules::usb::usbc_ctap_hid::ClientCtapHID<
|
||||
'static,
|
||||
'static,
|
||||
nrf52840::usbd::Usbd<'static>,
|
||||
>,
|
||||
capsules::usb::usbc_ctap_hid::ClientCtapHID::new(
|
||||
&nrf52840::usbd::USBD,
|
||||
capsules::usb::usbc_client::MAX_CTRL_PACKET_SIZE_NRF52840,
|
||||
VENDOR_ID,
|
||||
PRODUCT_ID,
|
||||
STRINGS,
|
||||
)
|
||||
);
|
||||
nrf52840::usbd::USBD.set_client(usb_ctap);
|
||||
let usb = components::usb_ctap::UsbCtapComponent::new(
|
||||
board_kernel,
|
||||
capsules::usb::usb_ctap::DRIVER_NUM,
|
||||
&nrf52840_peripherals.usbd,
|
||||
capsules::usb::usbc_client::MAX_CTRL_PACKET_SIZE_NRF52840,
|
||||
VENDOR_ID,
|
||||
PRODUCT_ID,
|
||||
STRINGS,
|
||||
)
|
||||
.finalize(components::usb_ctap_component_helper!(nrf52840::usbd::Usbd));
|
||||
|
||||
// Enable power events to be sent to USB controller
|
||||
nrf52840::power::POWER.set_usb_client(&nrf52840::usbd::USBD);
|
||||
nrf52840::power::POWER.enable_interrupts();
|
||||
nrf52_components::NrfClockComponent::new(&base_peripherals.clock).finalize(());
|
||||
|
||||
// Configure the USB userspace driver
|
||||
let usb_driver = static_init!(
|
||||
capsules::usb::usb_ctap::CtapUsbSyscallDriver<
|
||||
'static,
|
||||
'static,
|
||||
nrf52840::usbd::Usbd<'static>,
|
||||
>,
|
||||
capsules::usb::usb_ctap::CtapUsbSyscallDriver::new(
|
||||
usb_ctap,
|
||||
board_kernel.create_grant(&memory_allocation_capability)
|
||||
)
|
||||
);
|
||||
usb_ctap.set_client(usb_driver);
|
||||
usb_driver as &'static _
|
||||
};
|
||||
|
||||
nrf52_components::NrfClockComponent::new().finalize(());
|
||||
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
|
||||
.finalize(components::rr_component_helper!(NUM_PROCS));
|
||||
|
||||
let platform = Platform {
|
||||
button,
|
||||
ble_radio,
|
||||
ieee802154_radio,
|
||||
pconsole,
|
||||
console,
|
||||
led,
|
||||
gpio,
|
||||
rng,
|
||||
temp,
|
||||
alarm,
|
||||
analog_comparator,
|
||||
nvmc,
|
||||
usb,
|
||||
ipc: kernel::ipc::IPC::new(board_kernel, &memory_allocation_capability),
|
||||
ipc: kernel::ipc::IPC::new(
|
||||
board_kernel,
|
||||
kernel::ipc::DRIVER_NUM,
|
||||
&memory_allocation_capability,
|
||||
),
|
||||
scheduler,
|
||||
systick: cortexm4::systick::SysTick::new_with_calibration(64000000),
|
||||
};
|
||||
|
||||
platform.pconsole.start();
|
||||
let _ = platform.pconsole.start();
|
||||
debug!("Initialization complete. Entering main loop\r");
|
||||
debug!("{}", &nrf52840::ficr::FICR_INSTANCE);
|
||||
|
||||
/// These symbols are defined in the linker script.
|
||||
// These symbols are defined in the linker script.
|
||||
extern "C" {
|
||||
/// Beginning of the ROM region containing app images.
|
||||
static _sapps: u8;
|
||||
@@ -386,7 +430,7 @@ pub unsafe fn reset_handler() {
|
||||
static _eappmem: u8;
|
||||
}
|
||||
|
||||
kernel::procs::load_processes(
|
||||
kernel::process::load_processes(
|
||||
board_kernel,
|
||||
chip,
|
||||
core::slice::from_raw_parts(
|
||||
@@ -398,7 +442,7 @@ pub unsafe fn reset_handler() {
|
||||
&_eappmem as *const u8 as usize - &_sappmem as *const u8 as usize,
|
||||
),
|
||||
&mut PROCESSES,
|
||||
FAULT_RESPONSE,
|
||||
&FAULT_RESPONSE,
|
||||
&process_management_capability,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
@@ -406,13 +450,5 @@ pub unsafe fn reset_handler() {
|
||||
debug!("{:?}", err);
|
||||
});
|
||||
|
||||
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
|
||||
.finalize(components::rr_component_helper!(NUM_PROCS));
|
||||
board_kernel.kernel_loop(
|
||||
&platform,
|
||||
chip,
|
||||
Some(&platform.ipc),
|
||||
scheduler,
|
||||
&main_loop_capability,
|
||||
);
|
||||
board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
|
||||
}
|
||||
|
||||
17
boards/nordic/nrf52840dk_opensk/Cargo.toml
Normal file
17
boards/nordic/nrf52840dk_opensk/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "nrf52840dk_opensk"
|
||||
version = "0.1.0"
|
||||
authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
|
||||
build = "build.rs"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
components = { path = "../../components" }
|
||||
cortexm4 = { path = "../../../arch/cortex-m4" }
|
||||
capsules = { path = "../../../capsules" }
|
||||
kernel = { path = "../../../kernel" }
|
||||
nrf52840 = { path = "../../../chips/nrf52840" }
|
||||
nrf52_components = { path = "../nrf52_components" }
|
||||
|
||||
[features]
|
||||
vendor_hid = ["capsules/vendor_hid"]
|
||||
31
boards/nordic/nrf52840dk_opensk/Makefile
Normal file
31
boards/nordic/nrf52840dk_opensk/Makefile
Normal file
@@ -0,0 +1,31 @@
|
||||
# Makefile for building the tock kernel for the nRF development kit
|
||||
|
||||
TARGET=thumbv7em-none-eabi
|
||||
PLATFORM=nrf52840dk_opensk
|
||||
|
||||
include ../../Makefile.common
|
||||
|
||||
TOCKLOADER=tockloader
|
||||
|
||||
# Where in the SAM4L flash to load the kernel with `tockloader`
|
||||
KERNEL_ADDRESS=0x00000
|
||||
|
||||
# Upload programs over uart with tockloader
|
||||
ifdef PORT
|
||||
TOCKLOADER_GENERAL_FLAGS += --port $(PORT)
|
||||
endif
|
||||
|
||||
# Upload the kernel over JTAG
|
||||
.PHONY: flash
|
||||
flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin
|
||||
$(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) --board nrf52dk --jlink $<
|
||||
|
||||
# Upload the kernel over JTAG using OpenOCD
|
||||
.PHONY: flash-openocd
|
||||
flash-openocd: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin
|
||||
$(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) --board nrf52dk --openocd $<
|
||||
|
||||
# Upload the kernel over serial/bootloader
|
||||
.PHONY: program
|
||||
program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).hex
|
||||
$(error Cannot program nRF52840DK over USB. Use \`make flash\` and JTAG)
|
||||
65
boards/nordic/nrf52840dk_opensk/README.md
Normal file
65
boards/nordic/nrf52840dk_opensk/README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
Platform-Specific Instructions: nRF52840-DK
|
||||
===================================
|
||||
|
||||
This is an adapted nrf52840dk made to work with OpenSK.
|
||||
|
||||
The [nRF52840 Development
|
||||
Kit](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK) is a platform
|
||||
based around the nRF52840, an SoC with an ARM Cortex-M4 and a BLE
|
||||
radio. The kit is Arduino shield compatible and includes several
|
||||
buttons.
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, follow the [Tock Getting Started guide](../../../doc/Getting_Started.md)
|
||||
|
||||
JTAG is the preferred method to program. The development kit has an
|
||||
integrated JTAG debugger, you simply need to [install JTAG
|
||||
software](../../../doc/Getting_Started.md#loading-the-kernel-onto-a-board).
|
||||
|
||||
## Programming the kernel
|
||||
Once you have all software installed, you should be able to simply run
|
||||
make flash in this directory to install a fresh kernel.
|
||||
|
||||
## Programming user-level applications
|
||||
You can program an application over USB using the integrated JTAG and `tockloader`:
|
||||
|
||||
```bash
|
||||
$ cd libtock-c/examples/<app>
|
||||
$ make
|
||||
$ tockloader install --jlink --board nrf52dk
|
||||
```
|
||||
|
||||
The same options (`--jlink --board nrf52dk`) must be passed for other tockloader commands
|
||||
such as `erase-apps` or `list`.
|
||||
|
||||
Viewing console output on the nrf52840dk is slightly different from other boards. You must use
|
||||
```bash
|
||||
$ tockloader listen
|
||||
```
|
||||
**followed by a press of the reset button** in order to view console output starting from the boot
|
||||
sequence. Notably, you should not
|
||||
pass the `--jlink` option to `tockloader listen`.
|
||||
|
||||
## Console output
|
||||
|
||||
This board supports two methods for writing messages to a console interface
|
||||
(console driver for applications as well as debug statements in the kernel).
|
||||
|
||||
By default, messages are written to a UART interface over the GPIO pins `P0.05`
|
||||
to `P0.08` (see the [main.rs](src/main.rs) file).
|
||||
|
||||
If you don't have any UART cables or want to use a different interface, there is
|
||||
also a console over the Segger RTT protocol. This only requires a micro-USB
|
||||
cable on the USB debugging port (the same used to flash Tock on the board), and
|
||||
is enabled by setting the `USB_DEBUGGING` constant to `true` in the
|
||||
[main.rs](src/main.rs) file.
|
||||
This disables the UART interface.
|
||||
|
||||
For instructions about how to receive RTT messages on the host, see the
|
||||
[corresponding capsule](../../../capsules/src/segger_rtt.rs).
|
||||
|
||||
## Debugging
|
||||
|
||||
See the [nrf52dk README](../nrf52dk/README.md) for information about debugging
|
||||
the nRF52840dk.
|
||||
29
boards/nordic/nrf52840dk_opensk/build.rs
Normal file
29
boards/nordic/nrf52840dk_opensk/build.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=layout.ld");
|
||||
println!("cargo:rerun-if-changed=../../kernel_layout.ld");
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let dest_path = Path::new(&out_dir).join("locations.rs");
|
||||
fs::write(
|
||||
&dest_path,
|
||||
"
|
||||
static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 2] = [
|
||||
// We implement NUM_PAGES = 20 as 16 + 4 to satisfy the MPU.
|
||||
kernel::StorageLocation {
|
||||
address: 0xC0000,
|
||||
size: 0x10000, // 16 pages
|
||||
storage_type: kernel::StorageType::Store,
|
||||
},
|
||||
kernel::StorageLocation {
|
||||
address: 0xD0000,
|
||||
size: 0x4000, // 4 pages
|
||||
storage_type: kernel::StorageType::Store,
|
||||
},
|
||||
];
|
||||
"
|
||||
).unwrap();
|
||||
}
|
||||
25
boards/nordic/nrf52840dk_opensk/jtag/gdbinit_pca10040.jlink
Normal file
25
boards/nordic/nrf52840dk_opensk/jtag/gdbinit_pca10040.jlink
Normal file
@@ -0,0 +1,25 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
# J-LINK GDB SERVER initialization
|
||||
#
|
||||
# This connects to a GDB Server listening
|
||||
# for commands on localhost at tcp port 2331
|
||||
target remote localhost:2331
|
||||
monitor speed 30
|
||||
file ../../../../target/thumbv7em-none-eabi/release/nrf52dk
|
||||
monitor reset
|
||||
#
|
||||
# CPU core initialization (to be done by user)
|
||||
#
|
||||
# Set the processor mode
|
||||
# monitor reg cpsr = 0xd3
|
||||
# Set auto JTAG speed
|
||||
monitor speed auto
|
||||
# Setup GDB FOR FASTER DOWNLOADS
|
||||
set remote memory-write-packet-size 1024
|
||||
set remote memory-write-packet-size fixed
|
||||
# tui enable
|
||||
# layout split
|
||||
# layout service_pending_interrupts
|
||||
b reset_handler
|
||||
1
boards/nordic/nrf52840dk_opensk/jtag/jdbserver_pca10040.sh
Executable file
1
boards/nordic/nrf52840dk_opensk/jtag/jdbserver_pca10040.sh
Executable file
@@ -0,0 +1 @@
|
||||
JLinkGDBServer -device nrf52 -speed 1200 -if swd -AutoConnect 1 -port 2331
|
||||
2
boards/nordic/nrf52840dk_opensk/layout.ld
Normal file
2
boards/nordic/nrf52840dk_opensk/layout.ld
Normal file
@@ -0,0 +1,2 @@
|
||||
INCLUDE ../nrf52840_chip_layout.ld
|
||||
INCLUDE ../../kernel_layout.ld
|
||||
108
boards/nordic/nrf52840dk_opensk/src/io.rs
Normal file
108
boards/nordic/nrf52840dk_opensk/src/io.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use core::fmt::Write;
|
||||
use core::panic::PanicInfo;
|
||||
use cortexm4;
|
||||
use kernel::debug;
|
||||
use kernel::debug::IoWrite;
|
||||
use kernel::hil::led;
|
||||
use kernel::hil::uart;
|
||||
use kernel::hil::uart::Configure;
|
||||
use nrf52840::gpio::Pin;
|
||||
|
||||
use crate::CHIP;
|
||||
use crate::PROCESSES;
|
||||
use crate::PROCESS_PRINTER;
|
||||
|
||||
enum Writer {
|
||||
WriterUart(/* initialized */ bool),
|
||||
WriterRtt(&'static capsules::segger_rtt::SeggerRttMemory<'static>),
|
||||
}
|
||||
|
||||
static mut WRITER: Writer = Writer::WriterUart(false);
|
||||
|
||||
fn wait() {
|
||||
for _ in 0..100 {
|
||||
cortexm4::support::nop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the RTT memory buffer used to output panic messages.
|
||||
pub unsafe fn set_rtt_memory(
|
||||
rtt_memory: &'static mut capsules::segger_rtt::SeggerRttMemory<'static>,
|
||||
) {
|
||||
WRITER = Writer::WriterRtt(rtt_memory);
|
||||
}
|
||||
|
||||
impl Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
|
||||
self.write(s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IoWrite for Writer {
|
||||
fn write(&mut self, buf: &[u8]) {
|
||||
match self {
|
||||
Writer::WriterUart(ref mut initialized) => {
|
||||
// Here, we create a second instance of the Uarte struct.
|
||||
// This is okay because we only call this during a panic, and
|
||||
// we will never actually process the interrupts
|
||||
let uart = nrf52840::uart::Uarte::new();
|
||||
if !*initialized {
|
||||
*initialized = true;
|
||||
let _ = uart.configure(uart::Parameters {
|
||||
baud_rate: 115200,
|
||||
stop_bits: uart::StopBits::One,
|
||||
parity: uart::Parity::None,
|
||||
hw_flow_control: false,
|
||||
width: uart::Width::Eight,
|
||||
});
|
||||
}
|
||||
for &c in buf {
|
||||
unsafe {
|
||||
uart.send_byte(c);
|
||||
}
|
||||
while !uart.tx_ready() {}
|
||||
}
|
||||
}
|
||||
Writer::WriterRtt(rtt_memory) => {
|
||||
let up_buffer = unsafe { &*rtt_memory.get_up_buffer_ptr() };
|
||||
let buffer_len = up_buffer.length.get();
|
||||
let buffer = unsafe {
|
||||
core::slice::from_raw_parts_mut(
|
||||
up_buffer.buffer.get() as *mut u8,
|
||||
buffer_len as usize,
|
||||
)
|
||||
};
|
||||
|
||||
let mut write_position = up_buffer.write_position.get();
|
||||
|
||||
for &c in buf {
|
||||
buffer[write_position as usize] = c;
|
||||
write_position = (write_position + 1) % buffer_len;
|
||||
up_buffer.write_position.set(write_position);
|
||||
wait();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[no_mangle]
|
||||
#[panic_handler]
|
||||
/// Panic handler
|
||||
pub unsafe extern "C" fn panic_fmt(pi: &PanicInfo) -> ! {
|
||||
// The nRF52840DK LEDs (see back of board)
|
||||
let led_kernel_pin = &nrf52840::gpio::GPIOPin::new(Pin::P0_13);
|
||||
let led = &mut led::LedLow::new(led_kernel_pin);
|
||||
let writer = &mut WRITER;
|
||||
debug::panic(
|
||||
&mut [led],
|
||||
writer,
|
||||
pi,
|
||||
&cortexm4::support::nop,
|
||||
&PROCESSES,
|
||||
&CHIP,
|
||||
&PROCESS_PRINTER,
|
||||
)
|
||||
}
|
||||
569
boards/nordic/nrf52840dk_opensk/src/main.rs
Normal file
569
boards/nordic/nrf52840dk_opensk/src/main.rs
Normal file
@@ -0,0 +1,569 @@
|
||||
//! Tock kernel for the Nordic Semiconductor nRF52840 development kit (DK).
|
||||
//!
|
||||
//! It is based on nRF52840 SoC (Cortex M4 core with a BLE transceiver) with
|
||||
//! many exported I/O and peripherals.
|
||||
//!
|
||||
//! Pin Configuration
|
||||
//! -------------------
|
||||
//!
|
||||
//! ### `GPIO`
|
||||
//!
|
||||
//! | # | Pin | Ix | Header | Arduino |
|
||||
//! |----|-------|----|--------|---------|
|
||||
//! | 0 | P1.01 | 33 | P3 1 | D0 |
|
||||
//! | 1 | P1.02 | 34 | P3 2 | D1 |
|
||||
//! | 2 | P1.03 | 35 | P3 3 | D2 |
|
||||
//! | 3 | P1.04 | 36 | P3 4 | D3 |
|
||||
//! | 4 | P1.05 | 37 | P3 5 | D4 |
|
||||
//! | 5 | P1.06 | 38 | P3 6 | D5 |
|
||||
//! | 6 | P1.07 | 39 | P3 7 | D6 |
|
||||
//! | 7 | P1.08 | 40 | P3 8 | D7 |
|
||||
//! | 8 | P1.10 | 42 | P4 1 | D8 |
|
||||
//! | 9 | P1.11 | 43 | P4 2 | D9 |
|
||||
//! | 10 | P1.12 | 44 | P4 3 | D10 |
|
||||
//! | 11 | P1.13 | 45 | P4 4 | D11 |
|
||||
//! | 12 | P1.14 | 46 | P4 5 | D12 |
|
||||
//! | 13 | P1.15 | 47 | P4 6 | D13 |
|
||||
//! | 14 | P0.26 | 26 | P4 9 | D14 |
|
||||
//! | 15 | P0.27 | 27 | P4 10 | D15 |
|
||||
//!
|
||||
//! ### `GPIO` / Analog Inputs
|
||||
//!
|
||||
//! | # | Pin | Header | Arduino |
|
||||
//! |----|------------|--------|---------|
|
||||
//! | 16 | P0.03 AIN1 | P2 1 | A0 |
|
||||
//! | 17 | P0.04 AIN2 | P2 2 | A1 |
|
||||
//! | 18 | P0.28 AIN4 | P2 3 | A2 |
|
||||
//! | 19 | P0.29 AIN5 | P2 4 | A3 |
|
||||
//! | 20 | P0.30 AIN6 | P2 5 | A4 |
|
||||
//! | 21 | P0.31 AIN7 | P2 6 | A5 |
|
||||
//! | 22 | P0.02 AIN0 | P4 8 | AVDD |
|
||||
//!
|
||||
//! ### Onboard Functions
|
||||
//!
|
||||
//! | Pin | Header | Function |
|
||||
//! |-------|--------|----------|
|
||||
//! | P0.05 | P6 3 | UART RTS |
|
||||
//! | P0.06 | P6 4 | UART TXD |
|
||||
//! | P0.07 | P6 5 | UART CTS |
|
||||
//! | P0.08 | P6 6 | UART RXT |
|
||||
//! | P0.11 | P24 1 | Button 1 |
|
||||
//! | P0.12 | P24 2 | Button 2 |
|
||||
//! | P0.13 | P24 3 | LED 1 |
|
||||
//! | P0.14 | P24 4 | LED 2 |
|
||||
//! | P0.15 | P24 5 | LED 3 |
|
||||
//! | P0.16 | P24 6 | LED 4 |
|
||||
//! | P0.18 | P24 8 | Reset |
|
||||
//! | P0.19 | P24 9 | SPI CLK |
|
||||
//! | P0.20 | P24 10 | SPI MOSI |
|
||||
//! | P0.21 | P24 11 | SPI MISO |
|
||||
//! | P0.24 | P24 14 | Button 3 |
|
||||
//! | P0.25 | P24 15 | Button 4 |
|
||||
|
||||
#![no_std]
|
||||
// Disable this attribute when documenting, as a workaround for
|
||||
// https://github.com/rust-lang/rust/issues/62184.
|
||||
#![cfg_attr(not(doc), no_main)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use capsules::virtual_alarm::VirtualMuxAlarm;
|
||||
use core::env;
|
||||
use kernel::component::Component;
|
||||
use kernel::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
|
||||
use kernel::hil::led::LedLow;
|
||||
use kernel::hil::time::Counter;
|
||||
use kernel::platform::{KernelResources, SyscallDriverLookup, SyscallFilter};
|
||||
use kernel::scheduler::round_robin::RoundRobinSched;
|
||||
#[allow(unused_imports)]
|
||||
use kernel::{capabilities, create_capability, debug, debug_gpio, debug_verbose, static_init};
|
||||
use nrf52840::gpio::Pin;
|
||||
use nrf52840::interrupt_service::Nrf52840DefaultPeripherals;
|
||||
use nrf52_components::{self, UartChannel, UartPins};
|
||||
|
||||
// The nRF52840DK LEDs (see back of board)
|
||||
const LED1_PIN: Pin = Pin::P0_13;
|
||||
const LED2_PIN: Pin = Pin::P0_14;
|
||||
const LED3_PIN: Pin = Pin::P0_15;
|
||||
const LED4_PIN: Pin = Pin::P0_16;
|
||||
|
||||
// The nRF52840DK buttons (see back of board)
|
||||
const BUTTON1_PIN: Pin = Pin::P0_11;
|
||||
const BUTTON2_PIN: Pin = Pin::P0_12;
|
||||
const BUTTON3_PIN: Pin = Pin::P0_24;
|
||||
const BUTTON4_PIN: Pin = Pin::P0_25;
|
||||
const BUTTON_RST_PIN: Pin = Pin::P0_18;
|
||||
|
||||
const UART_RTS: Option<Pin> = Some(Pin::P0_05);
|
||||
const UART_TXD: Pin = Pin::P0_06;
|
||||
const UART_CTS: Option<Pin> = Some(Pin::P0_07);
|
||||
const UART_RXD: Pin = Pin::P0_08;
|
||||
|
||||
const SPI_MOSI: Pin = Pin::P0_20;
|
||||
const SPI_MISO: Pin = Pin::P0_21;
|
||||
const SPI_CLK: Pin = Pin::P0_19;
|
||||
|
||||
/// Debug Writer
|
||||
pub mod io;
|
||||
|
||||
// Whether to use UART debugging or Segger RTT (USB) debugging.
|
||||
// - Set to false to use UART.
|
||||
// - Set to true to use Segger RTT over USB.
|
||||
const USB_DEBUGGING: bool = true;
|
||||
|
||||
const VENDOR_ID: u16 = 0x1915; // Nordic Semiconductor
|
||||
const PRODUCT_ID: u16 = 0x521f; // nRF52840 Dongle (PCA10059)
|
||||
static STRINGS: &'static [&'static str] = &[
|
||||
// Manufacturer
|
||||
"Nordic Semiconductor ASA",
|
||||
// Product
|
||||
"OpenSK",
|
||||
// Serial number
|
||||
"v1.0",
|
||||
// Interface description + main HID string
|
||||
"FIDO2",
|
||||
// vendor HID string
|
||||
"Vendor HID",
|
||||
];
|
||||
|
||||
// State for loading and holding applications.
|
||||
// How should the kernel respond when a process faults.
|
||||
const FAULT_RESPONSE: kernel::process::PanicFaultPolicy = kernel::process::PanicFaultPolicy {};
|
||||
|
||||
// Number of concurrent processes this platform supports.
|
||||
const NUM_PROCS: usize = 8;
|
||||
|
||||
static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
|
||||
[None; NUM_PROCS];
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/locations.rs"));
|
||||
|
||||
static mut CHIP: Option<&'static nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>> = None;
|
||||
static mut PROCESS_PRINTER: Option<&'static kernel::process::ProcessPrinterText> = None;
|
||||
|
||||
/// Flash buffer for the custom nvmc driver
|
||||
static mut APP_FLASH_BUFFER: [u8; 0x1000] = [0; 0x1000];
|
||||
|
||||
/// Dummy buffer that causes the linker to reserve enough space for the stack.
|
||||
#[no_mangle]
|
||||
#[link_section = ".stack_buffer"]
|
||||
pub static mut STACK_MEMORY: [u8; 0x2000] = [0; 0x2000];
|
||||
|
||||
/// Supported drivers by the platform
|
||||
pub struct Platform {
|
||||
button: &'static capsules::button::Button<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
pconsole: &'static capsules::process_console::ProcessConsole<
|
||||
'static,
|
||||
VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
|
||||
components::process_console::Capability,
|
||||
>,
|
||||
console: &'static capsules::console::Console<'static>,
|
||||
gpio: &'static capsules::gpio::GPIO<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
led: &'static capsules::led::LedDriver<
|
||||
'static,
|
||||
kernel::hil::led::LedLow<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
4,
|
||||
>,
|
||||
rng: &'static capsules::rng::RngDriver<'static>,
|
||||
ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
|
||||
analog_comparator: &'static capsules::analog_comparator::AnalogComparator<
|
||||
'static,
|
||||
nrf52840::acomp::Comparator<'static>,
|
||||
>,
|
||||
alarm: &'static capsules::alarm::AlarmDriver<
|
||||
'static,
|
||||
capsules::virtual_alarm::VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
|
||||
>,
|
||||
nvmc: &'static nrf52840::nvmc::SyscallDriver,
|
||||
usb: &'static capsules::usb::usb_ctap::CtapUsbSyscallDriver<
|
||||
'static,
|
||||
'static,
|
||||
nrf52840::usbd::Usbd<'static>,
|
||||
>,
|
||||
scheduler: &'static RoundRobinSched<'static>,
|
||||
systick: cortexm4::systick::SysTick,
|
||||
}
|
||||
|
||||
impl SyscallDriverLookup for Platform {
|
||||
fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
|
||||
{
|
||||
match driver_num {
|
||||
capsules::console::DRIVER_NUM => f(Some(self.console)),
|
||||
capsules::gpio::DRIVER_NUM => f(Some(self.gpio)),
|
||||
capsules::alarm::DRIVER_NUM => f(Some(self.alarm)),
|
||||
capsules::led::DRIVER_NUM => f(Some(self.led)),
|
||||
capsules::button::DRIVER_NUM => f(Some(self.button)),
|
||||
capsules::rng::DRIVER_NUM => f(Some(self.rng)),
|
||||
capsules::analog_comparator::DRIVER_NUM => f(Some(self.analog_comparator)),
|
||||
nrf52840::nvmc::DRIVER_NUM => f(Some(self.nvmc)),
|
||||
capsules::usb::usb_ctap::DRIVER_NUM => f(Some(self.usb)),
|
||||
kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
|
||||
_ => f(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SyscallFilter for Platform {
|
||||
fn filter_syscall(
|
||||
&self,
|
||||
process: &dyn kernel::process::Process,
|
||||
syscall: &kernel::syscall::Syscall,
|
||||
) -> Result<(), kernel::errorcode::ErrorCode> {
|
||||
use kernel::syscall::Syscall;
|
||||
match *syscall {
|
||||
Syscall::Command {
|
||||
driver_number: nrf52840::nvmc::DRIVER_NUM,
|
||||
subdriver_number: cmd,
|
||||
arg0: ptr,
|
||||
arg1: len,
|
||||
} if (cmd == 2 || cmd == 3) && !process.fits_in_storage_location(ptr, len) => {
|
||||
Err(kernel::ErrorCode::INVAL)
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is in a separate, inline(never) function so that its stack frame is
|
||||
/// removed when this function returns. Otherwise, the stack space used for
|
||||
/// these static_inits is wasted.
|
||||
#[inline(never)]
|
||||
unsafe fn get_peripherals() -> &'static mut Nrf52840DefaultPeripherals<'static> {
|
||||
// Initialize chip peripheral drivers
|
||||
let nrf52840_peripherals = static_init!(
|
||||
Nrf52840DefaultPeripherals,
|
||||
Nrf52840DefaultPeripherals::new()
|
||||
);
|
||||
|
||||
nrf52840_peripherals
|
||||
}
|
||||
|
||||
impl KernelResources<nrf52840::chip::NRF52<'static, Nrf52840DefaultPeripherals<'static>>>
|
||||
for Platform
|
||||
{
|
||||
type SyscallDriverLookup = Self;
|
||||
type SyscallFilter = Self;
|
||||
type ProcessFault = ();
|
||||
type Scheduler = RoundRobinSched<'static>;
|
||||
type SchedulerTimer = cortexm4::systick::SysTick;
|
||||
type WatchDog = ();
|
||||
type ContextSwitchCallback = ();
|
||||
|
||||
fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
|
||||
&self
|
||||
}
|
||||
fn syscall_filter(&self) -> &Self::SyscallFilter {
|
||||
&self
|
||||
}
|
||||
fn process_fault(&self) -> &Self::ProcessFault {
|
||||
&()
|
||||
}
|
||||
fn scheduler(&self) -> &Self::Scheduler {
|
||||
self.scheduler
|
||||
}
|
||||
fn scheduler_timer(&self) -> &Self::SchedulerTimer {
|
||||
&self.systick
|
||||
}
|
||||
fn watchdog(&self) -> &Self::WatchDog {
|
||||
&()
|
||||
}
|
||||
fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
|
||||
&()
|
||||
}
|
||||
}
|
||||
|
||||
/// Main function called after RAM initialized.
|
||||
#[no_mangle]
|
||||
pub unsafe fn main() {
|
||||
// Loads relocations and clears BSS
|
||||
nrf52840::init();
|
||||
// Initialize chip peripheral drivers
|
||||
let nrf52840_peripherals = get_peripherals();
|
||||
|
||||
// set up circular peripheral dependencies
|
||||
nrf52840_peripherals.init();
|
||||
let base_peripherals = &nrf52840_peripherals.nrf52;
|
||||
|
||||
let uart_channel = if USB_DEBUGGING {
|
||||
// Initialize early so any panic beyond this point can use the RTT memory object.
|
||||
let mut rtt_memory_refs =
|
||||
components::segger_rtt::SeggerRttMemoryComponent::new().finalize(());
|
||||
|
||||
// XXX: This is inherently unsafe as it aliases the mutable reference to rtt_memory. This
|
||||
// aliases reference is only used inside a panic handler, which should be OK, but maybe we
|
||||
// should use a const reference to rtt_memory and leverage interior mutability instead.
|
||||
self::io::set_rtt_memory(&mut *rtt_memory_refs.get_rtt_memory_ptr());
|
||||
|
||||
UartChannel::Rtt(rtt_memory_refs)
|
||||
} else {
|
||||
UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD))
|
||||
};
|
||||
|
||||
let board_kernel = static_init!(
|
||||
kernel::Kernel,
|
||||
kernel::Kernel::new_with_storage(&PROCESSES, &STORAGE_LOCATIONS)
|
||||
);
|
||||
|
||||
let gpio = components::gpio::GpioComponent::new(
|
||||
board_kernel,
|
||||
capsules::gpio::DRIVER_NUM,
|
||||
components::gpio_component_helper!(
|
||||
nrf52840::gpio::GPIOPin,
|
||||
0 => &nrf52840_peripherals.gpio_port[Pin::P1_01],
|
||||
1 => &nrf52840_peripherals.gpio_port[Pin::P1_02],
|
||||
2 => &nrf52840_peripherals.gpio_port[Pin::P1_03],
|
||||
3 => &nrf52840_peripherals.gpio_port[Pin::P1_04],
|
||||
4 => &nrf52840_peripherals.gpio_port[Pin::P1_05],
|
||||
5 => &nrf52840_peripherals.gpio_port[Pin::P1_06],
|
||||
6 => &nrf52840_peripherals.gpio_port[Pin::P1_07],
|
||||
7 => &nrf52840_peripherals.gpio_port[Pin::P1_08],
|
||||
8 => &nrf52840_peripherals.gpio_port[Pin::P1_10],
|
||||
9 => &nrf52840_peripherals.gpio_port[Pin::P1_11],
|
||||
10 => &nrf52840_peripherals.gpio_port[Pin::P1_12],
|
||||
11 => &nrf52840_peripherals.gpio_port[Pin::P1_13],
|
||||
12 => &nrf52840_peripherals.gpio_port[Pin::P1_14],
|
||||
13 => &nrf52840_peripherals.gpio_port[Pin::P1_15],
|
||||
14 => &nrf52840_peripherals.gpio_port[Pin::P0_26],
|
||||
15 => &nrf52840_peripherals.gpio_port[Pin::P0_27]
|
||||
),
|
||||
)
|
||||
.finalize(components::gpio_component_buf!(nrf52840::gpio::GPIOPin));
|
||||
|
||||
let button = components::button::ButtonComponent::new(
|
||||
board_kernel,
|
||||
capsules::button::DRIVER_NUM,
|
||||
components::button_component_helper!(
|
||||
nrf52840::gpio::GPIOPin,
|
||||
(
|
||||
&nrf52840_peripherals.gpio_port[BUTTON1_PIN],
|
||||
kernel::hil::gpio::ActivationMode::ActiveLow,
|
||||
kernel::hil::gpio::FloatingState::PullUp
|
||||
), //13
|
||||
(
|
||||
&nrf52840_peripherals.gpio_port[BUTTON2_PIN],
|
||||
kernel::hil::gpio::ActivationMode::ActiveLow,
|
||||
kernel::hil::gpio::FloatingState::PullUp
|
||||
), //14
|
||||
(
|
||||
&nrf52840_peripherals.gpio_port[BUTTON3_PIN],
|
||||
kernel::hil::gpio::ActivationMode::ActiveLow,
|
||||
kernel::hil::gpio::FloatingState::PullUp
|
||||
), //15
|
||||
(
|
||||
&nrf52840_peripherals.gpio_port[BUTTON4_PIN],
|
||||
kernel::hil::gpio::ActivationMode::ActiveLow,
|
||||
kernel::hil::gpio::FloatingState::PullUp
|
||||
) //16
|
||||
),
|
||||
)
|
||||
.finalize(components::button_component_buf!(nrf52840::gpio::GPIOPin));
|
||||
|
||||
let led = components::led::LedsComponent::new().finalize(components::led_component_helper!(
|
||||
LedLow<'static, nrf52840::gpio::GPIOPin>,
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED2_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED3_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED4_PIN]),
|
||||
));
|
||||
|
||||
let chip = static_init!(
|
||||
nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>,
|
||||
nrf52840::chip::NRF52::new(nrf52840_peripherals)
|
||||
);
|
||||
CHIP = Some(chip);
|
||||
|
||||
nrf52_components::startup::NrfStartupComponent::new(
|
||||
false,
|
||||
BUTTON_RST_PIN,
|
||||
nrf52840::uicr::Regulator0Output::DEFAULT,
|
||||
&base_peripherals.nvmc,
|
||||
)
|
||||
.finalize(());
|
||||
|
||||
// Create capabilities that the board needs to call certain protected kernel
|
||||
// functions.
|
||||
let process_management_capability =
|
||||
create_capability!(capabilities::ProcessManagementCapability);
|
||||
let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
|
||||
let memory_allocation_capability = create_capability!(capabilities::MemoryAllocationCapability);
|
||||
let gpio_port = &nrf52840_peripherals.gpio_port;
|
||||
// Configure kernel debug gpios as early as possible
|
||||
kernel::debug::assign_gpios(
|
||||
Some(&gpio_port[LED1_PIN]),
|
||||
Some(&gpio_port[LED2_PIN]),
|
||||
Some(&gpio_port[LED3_PIN]),
|
||||
);
|
||||
|
||||
let rtc = &base_peripherals.rtc;
|
||||
let _ = rtc.start();
|
||||
let mux_alarm = components::alarm::AlarmMuxComponent::new(rtc)
|
||||
.finalize(components::alarm_mux_component_helper!(nrf52840::rtc::Rtc));
|
||||
let alarm = components::alarm::AlarmDriverComponent::new(
|
||||
board_kernel,
|
||||
capsules::alarm::DRIVER_NUM,
|
||||
mux_alarm,
|
||||
)
|
||||
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
|
||||
|
||||
let channel = nrf52_components::UartChannelComponent::new(
|
||||
uart_channel,
|
||||
mux_alarm,
|
||||
&base_peripherals.uarte0,
|
||||
)
|
||||
.finalize(());
|
||||
|
||||
let dynamic_deferred_call_clients =
|
||||
static_init!([DynamicDeferredCallClientState; 3], Default::default());
|
||||
let dynamic_deferred_caller = static_init!(
|
||||
DynamicDeferredCall,
|
||||
DynamicDeferredCall::new(dynamic_deferred_call_clients)
|
||||
);
|
||||
DynamicDeferredCall::set_global_instance(dynamic_deferred_caller);
|
||||
let process_printer =
|
||||
components::process_printer::ProcessPrinterTextComponent::new().finalize(());
|
||||
PROCESS_PRINTER = Some(process_printer);
|
||||
|
||||
// Create a shared UART channel for the console and for kernel debug.
|
||||
let uart_mux =
|
||||
components::console::UartMuxComponent::new(channel, 115200, dynamic_deferred_caller)
|
||||
.finalize(());
|
||||
|
||||
let pconsole = components::process_console::ProcessConsoleComponent::new(
|
||||
board_kernel,
|
||||
uart_mux,
|
||||
mux_alarm,
|
||||
process_printer,
|
||||
)
|
||||
.finalize(components::process_console_component_helper!(
|
||||
nrf52840::rtc::Rtc<'static>
|
||||
));
|
||||
|
||||
// Setup the console.
|
||||
let console = components::console::ConsoleComponent::new(
|
||||
board_kernel,
|
||||
capsules::console::DRIVER_NUM,
|
||||
uart_mux,
|
||||
)
|
||||
.finalize(components::console_component_helper!());
|
||||
// Create the debugger object that handles calls to `debug!()`.
|
||||
components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(());
|
||||
|
||||
let rng = components::rng::RngComponent::new(
|
||||
board_kernel,
|
||||
capsules::rng::DRIVER_NUM,
|
||||
&base_peripherals.trng,
|
||||
)
|
||||
.finalize(());
|
||||
|
||||
base_peripherals.spim0.configure(
|
||||
nrf52840::pinmux::Pinmux::new(SPI_MOSI as u32),
|
||||
nrf52840::pinmux::Pinmux::new(SPI_MISO as u32),
|
||||
nrf52840::pinmux::Pinmux::new(SPI_CLK as u32),
|
||||
);
|
||||
|
||||
// Initialize AC using AIN5 (P0.29) as VIN+ and VIN- as AIN0 (P0.02)
|
||||
// These are hardcoded pin assignments specified in the driver
|
||||
let analog_comparator = components::analog_comparator::AcComponent::new(
|
||||
&base_peripherals.acomp,
|
||||
components::acomp_component_helper!(
|
||||
nrf52840::acomp::Channel,
|
||||
&nrf52840::acomp::CHANNEL_AC0
|
||||
),
|
||||
board_kernel,
|
||||
capsules::analog_comparator::DRIVER_NUM,
|
||||
)
|
||||
.finalize(components::acomp_component_buf!(
|
||||
nrf52840::acomp::Comparator
|
||||
));
|
||||
|
||||
let nvmc = static_init!(
|
||||
nrf52840::nvmc::SyscallDriver,
|
||||
nrf52840::nvmc::SyscallDriver::new(
|
||||
&base_peripherals.nvmc,
|
||||
board_kernel.create_grant(nrf52840::nvmc::DRIVER_NUM, &memory_allocation_capability),
|
||||
dynamic_deferred_caller,
|
||||
&mut APP_FLASH_BUFFER,
|
||||
)
|
||||
);
|
||||
nvmc.set_deferred_handle(
|
||||
dynamic_deferred_caller
|
||||
.register(nvmc)
|
||||
.expect("no deferred call slot available for nvmc"),
|
||||
);
|
||||
|
||||
// Configure USB controller
|
||||
let usb = components::usb_ctap::UsbCtapComponent::new(
|
||||
board_kernel,
|
||||
capsules::usb::usb_ctap::DRIVER_NUM,
|
||||
&nrf52840_peripherals.usbd,
|
||||
capsules::usb::usbc_client::MAX_CTRL_PACKET_SIZE_NRF52840,
|
||||
VENDOR_ID,
|
||||
PRODUCT_ID,
|
||||
STRINGS,
|
||||
)
|
||||
.finalize(components::usb_ctap_component_helper!(nrf52840::usbd::Usbd));
|
||||
|
||||
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
|
||||
.finalize(components::rr_component_helper!(NUM_PROCS));
|
||||
|
||||
nrf52_components::NrfClockComponent::new(&base_peripherals.clock).finalize(());
|
||||
|
||||
let platform = Platform {
|
||||
button,
|
||||
pconsole,
|
||||
console,
|
||||
led,
|
||||
gpio,
|
||||
rng,
|
||||
alarm,
|
||||
analog_comparator,
|
||||
nvmc,
|
||||
usb,
|
||||
ipc: kernel::ipc::IPC::new(
|
||||
board_kernel,
|
||||
kernel::ipc::DRIVER_NUM,
|
||||
&memory_allocation_capability,
|
||||
),
|
||||
scheduler,
|
||||
systick: cortexm4::systick::SysTick::new_with_calibration(64000000),
|
||||
};
|
||||
|
||||
let _ = platform.pconsole.start();
|
||||
debug!("Initialization complete. Entering main loop\r");
|
||||
debug!("{}", &nrf52840::ficr::FICR_INSTANCE);
|
||||
|
||||
// These symbols are defined in the linker script.
|
||||
extern "C" {
|
||||
/// Beginning of the ROM region containing app images.
|
||||
static _sapps: u8;
|
||||
/// End of the ROM region containing app images.
|
||||
static _eapps: u8;
|
||||
/// Beginning of the RAM region for app memory.
|
||||
static mut _sappmem: u8;
|
||||
/// End of the RAM region for app memory.
|
||||
static _eappmem: u8;
|
||||
}
|
||||
|
||||
kernel::process::load_processes(
|
||||
board_kernel,
|
||||
chip,
|
||||
core::slice::from_raw_parts(
|
||||
&_sapps as *const u8,
|
||||
&_eapps as *const u8 as usize - &_sapps as *const u8 as usize,
|
||||
),
|
||||
core::slice::from_raw_parts_mut(
|
||||
&mut _sappmem as *mut u8,
|
||||
&_eappmem as *const u8 as usize - &_sappmem as *const u8 as usize,
|
||||
),
|
||||
&mut PROCESSES,
|
||||
&FAULT_RESPONSE,
|
||||
&process_management_capability,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
debug!("Error loading processes!");
|
||||
debug!("{:?}", err);
|
||||
});
|
||||
|
||||
board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
|
||||
}
|
||||
21
boards/nordic/nrf52840dk_opensk_a/Cargo.toml
Normal file
21
boards/nordic/nrf52840dk_opensk_a/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "nrf52840dk_opensk_a"
|
||||
version = "0.1.0"
|
||||
authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
|
||||
build = "build.rs"
|
||||
edition = "2018"
|
||||
|
||||
[[bin]]
|
||||
path = "../nrf52840dk_opensk/src/main.rs"
|
||||
name = "nrf52840dk_opensk_a"
|
||||
|
||||
[dependencies]
|
||||
components = { path = "../../components" }
|
||||
cortexm4 = { path = "../../../arch/cortex-m4" }
|
||||
capsules = { path = "../../../capsules" }
|
||||
kernel = { path = "../../../kernel" }
|
||||
nrf52840 = { path = "../../../chips/nrf52840" }
|
||||
nrf52_components = { path = "../nrf52_components" }
|
||||
|
||||
[features]
|
||||
vendor_hid = ["capsules/vendor_hid"]
|
||||
31
boards/nordic/nrf52840dk_opensk_a/Makefile
Normal file
31
boards/nordic/nrf52840dk_opensk_a/Makefile
Normal file
@@ -0,0 +1,31 @@
|
||||
# Makefile for building the tock kernel for the nRF development kit
|
||||
|
||||
TARGET=thumbv7em-none-eabi
|
||||
PLATFORM=nrf52840dk_opensk_a
|
||||
|
||||
include ../../Makefile.common
|
||||
|
||||
TOCKLOADER=tockloader
|
||||
|
||||
# Where in the SAM4L flash to load the kernel with `tockloader`
|
||||
KERNEL_ADDRESS=0x20000
|
||||
|
||||
# Upload programs over uart with tockloader
|
||||
ifdef PORT
|
||||
TOCKLOADER_GENERAL_FLAGS += --port $(PORT)
|
||||
endif
|
||||
|
||||
# Upload the kernel over JTAG
|
||||
.PHONY: flash
|
||||
flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin
|
||||
$(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) --board nrf52dk --jlink $<
|
||||
|
||||
# Upload the kernel over JTAG using OpenOCD
|
||||
.PHONY: flash-openocd
|
||||
flash-openocd: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin
|
||||
$(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) --board nrf52dk --openocd $<
|
||||
|
||||
# Upload the kernel over serial/bootloader
|
||||
.PHONY: program
|
||||
program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).hex
|
||||
$(error Cannot program nRF52840DK over USB. Use \`make flash\` and JTAG)
|
||||
11
boards/nordic/nrf52840dk_opensk_a/README.md
Normal file
11
boards/nordic/nrf52840dk_opensk_a/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
Platform-Specific Instructions: nRF52840-DK, partition A
|
||||
===================================
|
||||
|
||||
This is an upgrade partition for the adapted nrf52840dk in `../nrf52840dk_opensk`.
|
||||
|
||||
Compared to our regular board definition for the nrf52840dk, changes are:
|
||||
- a `layout.ld` with 128 kB for kernel and app
|
||||
- the matching kernel address in the `Makefile`
|
||||
- different `StorageLocation`s in `build.rs`
|
||||
|
||||
For everything else, please check the README in `../nrf52840dk_opensk`.
|
||||
45
boards/nordic/nrf52840dk_opensk_a/build.rs
Normal file
45
boards/nordic/nrf52840dk_opensk_a/build.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=layout.ld");
|
||||
println!("cargo:rerun-if-changed=../../kernel_layout.ld");
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let dest_path = Path::new(&out_dir).join("locations.rs");
|
||||
fs::write(
|
||||
&dest_path,
|
||||
"
|
||||
static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 5] = [
|
||||
// We implement NUM_PAGES = 20 as 16 + 4 to satisfy the MPU.
|
||||
kernel::StorageLocation {
|
||||
address: 0xC0000,
|
||||
size: 0x10000, // 16 pages
|
||||
storage_type: kernel::StorageType::Store,
|
||||
},
|
||||
kernel::StorageLocation {
|
||||
address: 0xD0000,
|
||||
size: 0x4000, // 4 pages
|
||||
storage_type: kernel::StorageType::Store,
|
||||
},
|
||||
// Partitions can also be split to maximize MPU happiness.
|
||||
kernel::StorageLocation {
|
||||
address: 0x4000,
|
||||
size: 0x2000,
|
||||
storage_type: kernel::StorageType::Partition,
|
||||
},
|
||||
kernel::StorageLocation {
|
||||
address: 0x60000,
|
||||
size: 0x20000,
|
||||
storage_type: kernel::StorageType::Partition,
|
||||
},
|
||||
kernel::StorageLocation {
|
||||
address: 0x80000,
|
||||
size: 0x20000,
|
||||
storage_type: kernel::StorageType::Partition,
|
||||
},
|
||||
];
|
||||
"
|
||||
).unwrap();
|
||||
}
|
||||
13
boards/nordic/nrf52840dk_opensk_a/layout.ld
Normal file
13
boards/nordic/nrf52840dk_opensk_a/layout.ld
Normal file
@@ -0,0 +1,13 @@
|
||||
/* Memory Space Definitions, 1M flash, 256K ram */
|
||||
MEMORY
|
||||
{
|
||||
rom (rx) : ORIGIN = 0x00020000, LENGTH = 128K
|
||||
prog (rx) : ORIGIN = 0x00040000, LENGTH = 128K
|
||||
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 256K
|
||||
}
|
||||
|
||||
MPU_MIN_ALIGN = 8K;
|
||||
PAGE_SIZE = 4K;
|
||||
|
||||
INCLUDE ../../kernel_layout.ld
|
||||
|
||||
21
boards/nordic/nrf52840dk_opensk_b/Cargo.toml
Normal file
21
boards/nordic/nrf52840dk_opensk_b/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "nrf52840dk_opensk_b"
|
||||
version = "0.1.0"
|
||||
authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
|
||||
build = "build.rs"
|
||||
edition = "2018"
|
||||
|
||||
[[bin]]
|
||||
path = "../nrf52840dk_opensk/src/main.rs"
|
||||
name = "nrf52840dk_opensk_b"
|
||||
|
||||
[dependencies]
|
||||
components = { path = "../../components" }
|
||||
cortexm4 = { path = "../../../arch/cortex-m4" }
|
||||
capsules = { path = "../../../capsules" }
|
||||
kernel = { path = "../../../kernel" }
|
||||
nrf52840 = { path = "../../../chips/nrf52840" }
|
||||
nrf52_components = { path = "../nrf52_components" }
|
||||
|
||||
[features]
|
||||
vendor_hid = ["capsules/vendor_hid"]
|
||||
31
boards/nordic/nrf52840dk_opensk_b/Makefile
Normal file
31
boards/nordic/nrf52840dk_opensk_b/Makefile
Normal file
@@ -0,0 +1,31 @@
|
||||
# Makefile for building the tock kernel for the nRF development kit
|
||||
|
||||
TARGET=thumbv7em-none-eabi
|
||||
PLATFORM=nrf52840dk_opensk_b
|
||||
|
||||
include ../../Makefile.common
|
||||
|
||||
TOCKLOADER=tockloader
|
||||
|
||||
# Where in the SAM4L flash to load the kernel with `tockloader`
|
||||
KERNEL_ADDRESS=0x60000
|
||||
|
||||
# Upload programs over uart with tockloader
|
||||
ifdef PORT
|
||||
TOCKLOADER_GENERAL_FLAGS += --port $(PORT)
|
||||
endif
|
||||
|
||||
# Upload the kernel over JTAG
|
||||
.PHONY: flash
|
||||
flash: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin
|
||||
$(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) --board nrf52dk --jlink $<
|
||||
|
||||
# Upload the kernel over JTAG using OpenOCD
|
||||
.PHONY: flash-openocd
|
||||
flash-openocd: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).bin
|
||||
$(TOCKLOADER) $(TOCKLOADER_GENERAL_FLAGS) flash --address $(KERNEL_ADDRESS) --board nrf52dk --openocd $<
|
||||
|
||||
# Upload the kernel over serial/bootloader
|
||||
.PHONY: program
|
||||
program: $(TOCK_ROOT_DIRECTORY)target/$(TARGET)/release/$(PLATFORM).hex
|
||||
$(error Cannot program nRF52840DK over USB. Use \`make flash\` and JTAG)
|
||||
11
boards/nordic/nrf52840dk_opensk_b/README.md
Normal file
11
boards/nordic/nrf52840dk_opensk_b/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
Platform-Specific Instructions: nRF52840-DK, partition B
|
||||
===================================
|
||||
|
||||
This is an upgrade partition for the adapted nrf52840dk in `../nrf52840dk_opensk`.
|
||||
|
||||
Compared to our regular board definition for the nrf52840dk, changes are:
|
||||
- a `layout.ld` with 128 kB for kernel and app
|
||||
- the matching kernel address in the `Makefile`
|
||||
- different `StorageLocation`s in `build.rs`
|
||||
|
||||
For everything else, please check the README in `../nrf52840dk_opensk`.
|
||||
45
boards/nordic/nrf52840dk_opensk_b/build.rs
Normal file
45
boards/nordic/nrf52840dk_opensk_b/build.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=layout.ld");
|
||||
println!("cargo:rerun-if-changed=../../kernel_layout.ld");
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let dest_path = Path::new(&out_dir).join("locations.rs");
|
||||
fs::write(
|
||||
&dest_path,
|
||||
"
|
||||
static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 5] = [
|
||||
// We implement NUM_PAGES = 20 as 16 + 4 to satisfy the MPU.
|
||||
kernel::StorageLocation {
|
||||
address: 0xC0000,
|
||||
size: 0x10000, // 16 pages
|
||||
storage_type: kernel::StorageType::Store,
|
||||
},
|
||||
kernel::StorageLocation {
|
||||
address: 0xD0000,
|
||||
size: 0x4000, // 4 pages
|
||||
storage_type: kernel::StorageType::Store,
|
||||
},
|
||||
// Partitions can also be split to maximize MPU happiness.
|
||||
kernel::StorageLocation {
|
||||
address: 0x4000,
|
||||
size: 0x2000,
|
||||
storage_type: kernel::StorageType::Partition,
|
||||
},
|
||||
kernel::StorageLocation {
|
||||
address: 0x20000,
|
||||
size: 0x20000,
|
||||
storage_type: kernel::StorageType::Partition,
|
||||
},
|
||||
kernel::StorageLocation {
|
||||
address: 0x40000,
|
||||
size: 0x20000,
|
||||
storage_type: kernel::StorageType::Partition,
|
||||
},
|
||||
];
|
||||
"
|
||||
).unwrap();
|
||||
}
|
||||
13
boards/nordic/nrf52840dk_opensk_b/layout.ld
Normal file
13
boards/nordic/nrf52840dk_opensk_b/layout.ld
Normal file
@@ -0,0 +1,13 @@
|
||||
/* Memory Space Definitions, 1M flash, 256K ram */
|
||||
MEMORY
|
||||
{
|
||||
rom (rx) : ORIGIN = 0x00060000, LENGTH = 128K
|
||||
prog (rx) : ORIGIN = 0x00080000, LENGTH = 128K
|
||||
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 256K
|
||||
}
|
||||
|
||||
MPU_MIN_ALIGN = 8K;
|
||||
PAGE_SIZE = 4K;
|
||||
|
||||
INCLUDE ../../kernel_layout.ld
|
||||
|
||||
2
bootloader/.cargo/config
Normal file
2
bootloader/.cargo/config
Normal file
@@ -0,0 +1,2 @@
|
||||
[target.thumbv7em-none-eabi]
|
||||
linker = "arm-none-eabi-gcc"
|
||||
282
bootloader/Cargo.lock
generated
Normal file
282
bootloader/Cargo.lock
generated
Normal file
@@ -0,0 +1,282 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aligned"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a785a543aea40f5e4e2e93bb2655d31bc21bb391fff65697150973e383f16bb"
|
||||
dependencies = [
|
||||
"as-slice",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "as-slice"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0"
|
||||
dependencies = [
|
||||
"generic-array 0.12.4",
|
||||
"generic-array 0.13.3",
|
||||
"generic-array 0.14.7",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bare-metal"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitfield"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
|
||||
|
||||
[[package]]
|
||||
name = "bootloader"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"cortex-m 0.6.7",
|
||||
"cortex-m-rt",
|
||||
"cortex-m-rt-macros",
|
||||
"panic-abort",
|
||||
"rtt-target",
|
||||
"tock-registers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9075300b07c6a56263b9b582c214d0ff037b00d45ec9fde1cc711490c56f1bb9"
|
||||
dependencies = [
|
||||
"aligned",
|
||||
"bare-metal",
|
||||
"bitfield",
|
||||
"cortex-m 0.7.7",
|
||||
"volatile-register",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m"
|
||||
version = "0.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
|
||||
dependencies = [
|
||||
"bare-metal",
|
||||
"bitfield",
|
||||
"embedded-hal",
|
||||
"volatile-register",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1"
|
||||
dependencies = [
|
||||
"cortex-m-rt-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cortex-m-rt-macros"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
|
||||
dependencies = [
|
||||
"nb 0.1.3",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
||||
dependencies = [
|
||||
"nb 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||
|
||||
[[package]]
|
||||
name = "panic-abort"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e20e6499bbbc412f280b04a42346b356c6fa0753d5fd22b7bd752ff34c778ee"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rtt-target"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "065d6058bb1204f51a562a67209e1817cf714759d5cf845aa45c75fa7b0b9d9b"
|
||||
dependencies = [
|
||||
"cortex-m 0.7.7",
|
||||
"ufmt-write",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tock-registers"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ee8fba06c1f4d0b396ef61a54530bb6b28f0dc61c38bc8bc5a5a48161e6282e"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "ufmt-write"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "vcell"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "volatile-register"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
|
||||
dependencies = [
|
||||
"vcell",
|
||||
]
|
||||
29
bootloader/Cargo.toml
Normal file
29
bootloader/Cargo.toml
Normal file
@@ -0,0 +1,29 @@
|
||||
[package]
|
||||
name = "bootloader"
|
||||
version = "0.1.0"
|
||||
authors = [
|
||||
"Fabian Kaczmarczyck <kaczmarczyck@google.com>",
|
||||
]
|
||||
build = "build.rs"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
byteorder = { version = "1", default-features = false }
|
||||
cortex-m = "^0.6.0"
|
||||
cortex-m-rt = "*"
|
||||
cortex-m-rt-macros = "*"
|
||||
panic-abort = "0.3.2"
|
||||
rtt-target = { version = "*", features = ["cortex-m"] }
|
||||
tock-registers = "0.7.0"
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
lto = true
|
||||
opt-level = 3
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
lto = true
|
||||
# Level "z" may decrease the binary size more of necessary.
|
||||
opt-level = 3
|
||||
36
bootloader/README.md
Normal file
36
bootloader/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# OpenSK Bootloader
|
||||
|
||||
This bootloader supports upgradability for OpenSK. Its functionality is to
|
||||
|
||||
- check images on A/B partitions,
|
||||
- boot the most recent valid partition.
|
||||
|
||||
## How to use
|
||||
|
||||
The bootloader is built and deployed by OpenSK's `deploy.py`. If your board
|
||||
defines a metadata address, it is detected as an upgradable board and this
|
||||
bootloader is flashed to memory address 0.
|
||||
|
||||
## How to debug
|
||||
|
||||
The bootloader prints debug message over RTT when compiled in debug mode. Using
|
||||
`nrfjprog` for flashing and inspecting memory is recommended for debugging.
|
||||
|
||||
```shell
|
||||
RUSTFLAGS="-C link-arg=-Wl,-Tlink.x -C link-arg=-nostartfiles" \
|
||||
cargo build --target thumbv7em-none-eabi
|
||||
llvm-objcopy -O ihex target/thumbv7em-none-eabi/debug/bootloader \
|
||||
target/thumbv7em-none-eabi/debug/bootloader.hex
|
||||
nrfjprog --program target/thumbv7em-none-eabi/debug/bootloader.hex \
|
||||
--sectorerase -f nrf52 --reset
|
||||
```
|
||||
|
||||
To read the debug messages, open two terminals for:
|
||||
|
||||
```shell
|
||||
JLinkRTTLogger -device NRF52840_XXAA -if swd -speed 1000 -RTTchannel 0
|
||||
JLinkRTTClient
|
||||
```
|
||||
|
||||
The first command also logs the output to a file. The second shows all output in
|
||||
real time.
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019 Google LLC
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,8 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub const ATTESTATION_PRIVATE_KEY_LENGTH: usize = 32;
|
||||
pub const AAGUID_LENGTH: usize = 16;
|
||||
|
||||
pub const AAGUID: &[u8; AAGUID_LENGTH] =
|
||||
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_aaguid.bin"));
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=memory.x");
|
||||
}
|
||||
21
bootloader/memory.x
Normal file
21
bootloader/memory.x
Normal file
@@ -0,0 +1,21 @@
|
||||
/* Copyright 2021 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16K
|
||||
RAM (rwx) : ORIGIN = 0x20020000, LENGTH = 128K
|
||||
}
|
||||
|
||||
118
bootloader/src/bitfields.rs
Normal file
118
bootloader/src/bitfields.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright 2020-2022 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use tock_registers::register_bitfields;
|
||||
|
||||
register_bitfields! [u32,
|
||||
// Generic or shared bitfields
|
||||
pub Task [
|
||||
ENABLE OFFSET(0) NUMBITS(1)
|
||||
],
|
||||
|
||||
pub Byte [
|
||||
VALUE OFFSET(0) NUMBITS(8)
|
||||
],
|
||||
|
||||
pub Busy [
|
||||
/// Asserted when AES_BUSY or DES_BUSY or HASH_BUSY are asserted or when the DIN FIFO is not empty
|
||||
BUSY OFFSET(0) NUMBITS(1) [
|
||||
Ready = 0,
|
||||
Busy = 1
|
||||
]
|
||||
],
|
||||
|
||||
// CC_CTL register bitfields
|
||||
pub CryptoMode [
|
||||
/// Determines the active cryptographic engine
|
||||
MODE OFFSET(0) NUMBITS(5) [
|
||||
Bypass = 0,
|
||||
Aes = 1,
|
||||
AesToHash = 2,
|
||||
AesAndHash = 3,
|
||||
Des = 4,
|
||||
DesToHash = 5,
|
||||
DesAndHash = 6,
|
||||
Hash = 7,
|
||||
AesMacAndBypass = 9,
|
||||
AesToHashAndDout = 10
|
||||
]
|
||||
],
|
||||
|
||||
// HOST_RGF register bitfields
|
||||
pub Interrupts [
|
||||
/// This interrupt is asserted when all data was delivered to DIN buffer from SRAM
|
||||
SRAM_TO_DIN OFFSET(4) NUMBITS(1),
|
||||
/// This interrupt is asserted when all data was delivered to SRAM buffer from DOUT
|
||||
DOUT_TO_SRAM OFFSET(5) NUMBITS(1),
|
||||
/// This interrupt is asserted when all data was delivered to DIN buffer from memory
|
||||
MEM_TO_DIN OFFSET(6) NUMBITS(1),
|
||||
/// This interrupt is asserted when all data was delivered to memory buffer from DOUT
|
||||
DOUT_TO_MEM OFFSET(7) NUMBITS(1),
|
||||
AXI_ERROR OFFSET(8) NUMBITS(1),
|
||||
/// The PKA end of operation interrupt status
|
||||
PKA_EXP OFFSET(9) NUMBITS(1),
|
||||
/// The RNG interrupt status
|
||||
RNG OFFSET(10) NUMBITS(1),
|
||||
/// The GPR interrupt status
|
||||
SYM_DMA_COMPLETED OFFSET(11) NUMBITS(1)
|
||||
],
|
||||
|
||||
pub RgfEndianness [
|
||||
/// DOUT write endianness
|
||||
DOUT_WR_BG OFFSET(3) NUMBITS(1) [
|
||||
LittleEndian = 0,
|
||||
BigEndian = 1
|
||||
],
|
||||
/// DIN write endianness
|
||||
DIN_RD_BG OFFSET(7) NUMBITS(1) [
|
||||
LittleEndian = 0,
|
||||
BigEndian = 1
|
||||
],
|
||||
/// DOUT write word endianness
|
||||
DOUT_WR_WBG OFFSET(11) NUMBITS(1) [
|
||||
LittleEndian = 0,
|
||||
BigEndian = 1
|
||||
],
|
||||
/// DIN write word endianness
|
||||
DIN_RD_WBG OFFSET(15) NUMBITS(1) [
|
||||
LittleEndian = 0,
|
||||
BigEndian = 1
|
||||
]
|
||||
],
|
||||
|
||||
// DIN and DOUT register bitfields
|
||||
pub LliWord1 [
|
||||
/// Total number of bytes to read using DMA in this entry
|
||||
BYTES_NUM OFFSET(0) NUMBITS(30),
|
||||
/// Indicates the first LLI entry
|
||||
FIRST OFFSET(30) NUMBITS(1),
|
||||
/// Indicates the last LLI entry
|
||||
LAST OFFSET(31) NUMBITS(1)
|
||||
],
|
||||
|
||||
pub HashControl [
|
||||
// bit 2 is reserved but to simplify the logic we include it in the bitfield.
|
||||
MODE OFFSET(0) NUMBITS(4) [
|
||||
MD5 = 0,
|
||||
SHA1 = 1,
|
||||
SHA256 = 2,
|
||||
SHA224 = 10
|
||||
]
|
||||
],
|
||||
|
||||
pub PaddingConfig [
|
||||
/// Enable Padding generation. must be reset upon completion of padding.
|
||||
DO_PAD OFFSET(2) NUMBITS(1)
|
||||
]
|
||||
];
|
||||
284
bootloader/src/crypto_cell.rs
Normal file
284
bootloader/src/crypto_cell.rs
Normal file
@@ -0,0 +1,284 @@
|
||||
// Copyright 2019-2022 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! CryptoCell 310
|
||||
//!
|
||||
//! Author
|
||||
//! -------------------
|
||||
//!
|
||||
//! * Author: Jean-Michel Picod <jmichel@google.com>
|
||||
//! * Date: October 1 2019
|
||||
|
||||
use super::bitfields;
|
||||
use super::registers::{CryptoCellRegisters, NordicCC310Registers};
|
||||
use super::static_ref::StaticRef;
|
||||
use core::cell::Cell;
|
||||
#[cfg(debug_assertions)]
|
||||
use rtt_target::rprintln;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
|
||||
const SHA256_INIT_VALUE: [u32; 8] = [
|
||||
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
|
||||
];
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum DigestAlgorithm {
|
||||
Sha256 = 2,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum OperationMode {
|
||||
Idle,
|
||||
Hash,
|
||||
}
|
||||
|
||||
pub struct CryptoCell310 {
|
||||
registers: StaticRef<CryptoCellRegisters>,
|
||||
power: StaticRef<NordicCC310Registers>,
|
||||
current_op: Cell<OperationMode>,
|
||||
|
||||
hash_ctx: Cell<[u32; 8]>,
|
||||
hash_total_size: Cell<u64>,
|
||||
}
|
||||
|
||||
const CC310_BASE: StaticRef<CryptoCellRegisters> =
|
||||
unsafe { StaticRef::new(0x5002B000 as *const CryptoCellRegisters) };
|
||||
const CC310_POWER: StaticRef<NordicCC310Registers> =
|
||||
unsafe { StaticRef::new(0x5002A500 as *const NordicCC310Registers) };
|
||||
|
||||
// Identification "signature" for CryptoCell. According to the documentation, the value
|
||||
// held by this register is a fixed value, used by Host driver to verify CryptoCell presence
|
||||
// at this address.
|
||||
// This value was read from a CryptoCell-310 on a nRF52840-dongle kit.
|
||||
const CC310_SIGNATURE: u32 = 0x20E00000;
|
||||
|
||||
impl CryptoCell310 {
|
||||
/// Creates a new instance of cryptocell state.
|
||||
pub const fn new() -> Self {
|
||||
CryptoCell310 {
|
||||
registers: CC310_BASE,
|
||||
power: CC310_POWER,
|
||||
current_op: Cell::new(OperationMode::Idle),
|
||||
|
||||
hash_ctx: Cell::new(SHA256_INIT_VALUE),
|
||||
hash_total_size: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn enable(&self) {
|
||||
self.power.enable.write(bitfields::Task::ENABLE::SET);
|
||||
for _i in 1..10 {
|
||||
let read_signature = self.registers.host_rgf.signature.get();
|
||||
if read_signature != CC310_SIGNATURE {
|
||||
#[cfg(debug_assertions)]
|
||||
rprintln!(
|
||||
"[loop {}] Invalid CC310 signature. Expected {}, got {}\n",
|
||||
_i,
|
||||
CC310_SIGNATURE,
|
||||
read_signature
|
||||
);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if self.registers.host_rgf.signature.get() != CC310_SIGNATURE {
|
||||
panic!("Failed to initialize CC310");
|
||||
}
|
||||
// Make sure everything is set to little endian
|
||||
self.registers.host_rgf.endian.write(
|
||||
bitfields::RgfEndianness::DOUT_WR_BG::LittleEndian
|
||||
+ bitfields::RgfEndianness::DIN_RD_BG::LittleEndian
|
||||
+ bitfields::RgfEndianness::DOUT_WR_WBG::LittleEndian
|
||||
+ bitfields::RgfEndianness::DIN_RD_WBG::LittleEndian,
|
||||
);
|
||||
// Always start the clock for DMA engine. It's too hard to keep
|
||||
// track of which submodule needs DMA otherwise.
|
||||
self.registers
|
||||
.misc
|
||||
.dma_clk_enable
|
||||
.write(bitfields::Task::ENABLE::SET);
|
||||
self.registers.host_rgf.interrupt_mask.write(
|
||||
bitfields::Interrupts::SRAM_TO_DIN::CLEAR
|
||||
+ bitfields::Interrupts::DOUT_TO_SRAM::CLEAR
|
||||
+ bitfields::Interrupts::MEM_TO_DIN::CLEAR
|
||||
+ bitfields::Interrupts::DOUT_TO_MEM::CLEAR
|
||||
+ bitfields::Interrupts::AXI_ERROR::SET
|
||||
+ bitfields::Interrupts::PKA_EXP::SET
|
||||
+ bitfields::Interrupts::RNG::SET
|
||||
+ bitfields::Interrupts::SYM_DMA_COMPLETED::CLEAR,
|
||||
);
|
||||
}
|
||||
|
||||
fn disable(&self) {
|
||||
self.registers.host_rgf.interrupt_mask.set(0);
|
||||
self.power.enable.write(bitfields::Task::ENABLE::CLEAR);
|
||||
self.registers
|
||||
.misc
|
||||
.dma_clk_enable
|
||||
.write(bitfields::Task::ENABLE::CLEAR);
|
||||
}
|
||||
|
||||
fn clear_data(&self) {
|
||||
let mut ctx = self.hash_ctx.get();
|
||||
ctx.iter_mut().for_each(|b| *b = 0);
|
||||
self.hash_ctx.set(ctx);
|
||||
self.hash_total_size.set(0);
|
||||
}
|
||||
|
||||
/// Adds data to the current hash computation.
|
||||
///
|
||||
/// You have to know in advance if is this is going to be the last block, and indicate that
|
||||
/// correctly. Sizes of chunks before the last need to be multiples of 64.
|
||||
pub fn update(&self, data: &[u8], is_last_block: bool) {
|
||||
// Start CryptoCell
|
||||
self.enable();
|
||||
|
||||
while self.registers.ctrl.hash_busy.is_set(bitfields::Busy::BUSY) {}
|
||||
while self
|
||||
.registers
|
||||
.ctrl
|
||||
.crypto_busy
|
||||
.is_set(bitfields::Busy::BUSY)
|
||||
{}
|
||||
while self
|
||||
.registers
|
||||
.din
|
||||
.mem_dma_busy
|
||||
.is_set(bitfields::Busy::BUSY)
|
||||
{}
|
||||
|
||||
// Start HASH module and configure it
|
||||
self.current_op.set(OperationMode::Hash);
|
||||
self.registers
|
||||
.misc
|
||||
.hash_clk_enable
|
||||
.write(bitfields::Task::ENABLE::SET);
|
||||
self.registers
|
||||
.ctrl
|
||||
.crypto_ctl
|
||||
.write(bitfields::CryptoMode::MODE::Hash);
|
||||
self.registers
|
||||
.hash
|
||||
.padding
|
||||
.write(bitfields::Task::ENABLE::SET);
|
||||
let size = self.hash_total_size.get();
|
||||
self.registers.hash.hash_len_lsb.set(size as u32);
|
||||
self.registers
|
||||
.hash
|
||||
.hash_len_msb
|
||||
.set(size.wrapping_shr(32) as u32);
|
||||
self.registers
|
||||
.hash
|
||||
.control
|
||||
.set(DigestAlgorithm::Sha256 as u32);
|
||||
|
||||
// Digest must be set backwards because writing to HASH[0]
|
||||
// starts computation
|
||||
let mut digest = self.hash_ctx.get();
|
||||
for i in (0..digest.len()).rev() {
|
||||
self.registers.hash.hash[i].set(digest[i]);
|
||||
}
|
||||
while self.registers.ctrl.hash_busy.is_set(bitfields::Busy::BUSY) {}
|
||||
|
||||
// Process data
|
||||
if !data.is_empty() {
|
||||
if is_last_block {
|
||||
self.registers
|
||||
.hash
|
||||
.auto_hw_padding
|
||||
.write(bitfields::Task::ENABLE::SET);
|
||||
}
|
||||
self.registers.din.src_lli_word0.set(data.as_ptr() as u32);
|
||||
self.registers
|
||||
.din
|
||||
.src_lli_word1
|
||||
.write(bitfields::LliWord1::BYTES_NUM.val(data.len() as u32));
|
||||
while !self
|
||||
.registers
|
||||
.host_rgf
|
||||
.interrupts
|
||||
.is_set(bitfields::Interrupts::MEM_TO_DIN)
|
||||
{}
|
||||
self.registers
|
||||
.host_rgf
|
||||
.interrupt_clear
|
||||
.write(bitfields::Interrupts::MEM_TO_DIN::SET);
|
||||
} else {
|
||||
// use DO_PAD to complete padding of previous operation
|
||||
self.registers
|
||||
.hash
|
||||
.pad_config
|
||||
.write(bitfields::PaddingConfig::DO_PAD::SET);
|
||||
}
|
||||
while self
|
||||
.registers
|
||||
.ctrl
|
||||
.crypto_busy
|
||||
.is_set(bitfields::Busy::BUSY)
|
||||
{}
|
||||
while self
|
||||
.registers
|
||||
.din
|
||||
.mem_dma_busy
|
||||
.is_set(bitfields::Busy::BUSY)
|
||||
{}
|
||||
|
||||
// Update context and total size
|
||||
for i in (0..digest.len()).rev() {
|
||||
digest[i] = self.registers.hash.hash[i].get();
|
||||
}
|
||||
self.hash_ctx.set(digest);
|
||||
let new_size: u64 = ((self.registers.hash.hash_len_msb.get() as u64) << 32)
|
||||
+ (self.registers.hash.hash_len_lsb.get() as u64);
|
||||
self.hash_total_size.set(new_size);
|
||||
|
||||
// Disable HASH module
|
||||
self.registers
|
||||
.hash
|
||||
.padding
|
||||
.write(bitfields::Task::ENABLE::SET);
|
||||
self.registers
|
||||
.hash
|
||||
.auto_hw_padding
|
||||
.write(bitfields::Task::ENABLE::CLEAR);
|
||||
self.registers
|
||||
.hash
|
||||
.pad_config
|
||||
.write(bitfields::PaddingConfig::DO_PAD::CLEAR);
|
||||
while self
|
||||
.registers
|
||||
.ctrl
|
||||
.crypto_busy
|
||||
.is_set(bitfields::Busy::BUSY)
|
||||
{}
|
||||
self.registers
|
||||
.misc
|
||||
.hash_clk_enable
|
||||
.write(bitfields::Task::ENABLE::CLEAR);
|
||||
|
||||
self.disable();
|
||||
}
|
||||
|
||||
/// Clears the data for potential reuse, and returns the result.
|
||||
pub fn finalize_and_clear(&self) -> [u8; 32] {
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
let words = self.hash_ctx.get();
|
||||
let mut bytes = [0u8; 32];
|
||||
for (i, word) in words.iter().enumerate() {
|
||||
BigEndian::write_u32(&mut bytes[4 * i..4 * i + 4], *word);
|
||||
}
|
||||
self.clear_data();
|
||||
bytes
|
||||
}
|
||||
}
|
||||
178
bootloader/src/main.rs
Normal file
178
bootloader/src/main.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
// Copyright 2021-2022 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
mod bitfields;
|
||||
mod crypto_cell;
|
||||
mod registers;
|
||||
mod static_ref;
|
||||
|
||||
extern crate cortex_m;
|
||||
extern crate cortex_m_rt as rt;
|
||||
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use core::convert::TryInto;
|
||||
use core::ptr;
|
||||
use cortex_m::asm;
|
||||
use panic_abort as _;
|
||||
use rt::entry;
|
||||
#[cfg(debug_assertions)]
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
|
||||
/// Size of a flash page in bytes.
|
||||
const PAGE_SIZE: usize = 0x1000;
|
||||
const METADATA_SIGN_OFFSET: usize = 0x800;
|
||||
|
||||
/// A flash page.
|
||||
type Page = [u8; PAGE_SIZE];
|
||||
|
||||
/// Reads a page of memory.
|
||||
unsafe fn read_page(address: usize) -> Page {
|
||||
debug_assert!(address % PAGE_SIZE == 0);
|
||||
let address_pointer = address as *const Page;
|
||||
ptr::read(address_pointer)
|
||||
}
|
||||
|
||||
/// Parsed metadata for a firmware partition.
|
||||
struct Metadata {
|
||||
checksum: [u8; 32],
|
||||
_signature: [u8; 64],
|
||||
version: u64,
|
||||
address: u32,
|
||||
}
|
||||
|
||||
/// Reads the metadata from a flash page.
|
||||
impl From<Page> for Metadata {
|
||||
fn from(page: Page) -> Self {
|
||||
Metadata {
|
||||
checksum: page[0..32].try_into().unwrap(),
|
||||
_signature: page[32..96].try_into().unwrap(),
|
||||
version: LittleEndian::read_u64(&page[METADATA_SIGN_OFFSET..][..8]),
|
||||
address: LittleEndian::read_u32(&page[METADATA_SIGN_OFFSET + 8..][..4]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Location of a firmware partition's data.
|
||||
struct BootPartition {
|
||||
firmware_address: usize,
|
||||
metadata_address: usize,
|
||||
}
|
||||
|
||||
impl BootPartition {
|
||||
const FIRMWARE_LENGTH: usize = 0x00040000;
|
||||
|
||||
/// Reads the metadata, returns the firmware version if all checks pass.
|
||||
pub fn read_version(&self) -> Result<u64, ()> {
|
||||
let metadata_page = unsafe { read_page(self.metadata_address) };
|
||||
let hash_value = self.compute_upgrade_hash(&metadata_page);
|
||||
let metadata = Metadata::from(metadata_page);
|
||||
if self.firmware_address != metadata.address as usize {
|
||||
#[cfg(debug_assertions)]
|
||||
rprintln!(
|
||||
"Partition address mismatch: expected 0x{:08X}, metadata 0x{:08X}",
|
||||
self.firmware_address,
|
||||
metadata.address as usize
|
||||
);
|
||||
return Err(());
|
||||
}
|
||||
if hash_value != metadata.checksum {
|
||||
#[cfg(debug_assertions)]
|
||||
rprintln!("Hash mismatch");
|
||||
return Err(());
|
||||
}
|
||||
Ok(metadata.version)
|
||||
}
|
||||
|
||||
/// Computes the SHA256 of metadata information and partition data.
|
||||
///
|
||||
/// Assumes that firmware address and length are divisible by the page size.
|
||||
/// This is the hardware implementation on the cryptocell.
|
||||
#[allow(clippy::assertions_on_constants)]
|
||||
fn compute_upgrade_hash(&self, metadata_page: &[u8]) -> [u8; 32] {
|
||||
debug_assert!(self.firmware_address % PAGE_SIZE == 0);
|
||||
debug_assert!(BootPartition::FIRMWARE_LENGTH % PAGE_SIZE == 0);
|
||||
let cc310 = crypto_cell::CryptoCell310::new();
|
||||
cc310.update(&metadata_page[METADATA_SIGN_OFFSET..], false);
|
||||
for page_offset in (0..BootPartition::FIRMWARE_LENGTH).step_by(PAGE_SIZE) {
|
||||
let page = unsafe { read_page(self.firmware_address + page_offset) };
|
||||
cc310.update(
|
||||
&page,
|
||||
page_offset + PAGE_SIZE == BootPartition::FIRMWARE_LENGTH,
|
||||
);
|
||||
}
|
||||
cc310.finalize_and_clear()
|
||||
}
|
||||
|
||||
/// Jump to the firmware.
|
||||
pub fn boot(&self) -> ! {
|
||||
let address = self.firmware_address;
|
||||
|
||||
// Clear any pending Cryptocell interrupt in NVIC
|
||||
let peripherals = cortex_m::Peripherals::take().unwrap();
|
||||
unsafe {
|
||||
// We could only clear cryptocell interrupts, but let's clean up before booting.
|
||||
// Example code to clear more specifically:
|
||||
// const CC310_IRQ: u16 = 42;
|
||||
// peripherals.NVIC.icpr[usize::from(CC310_IRQ / 32)].write(1 << (CC310_IRQ % 32));
|
||||
peripherals.NVIC.icer[0].write(0xffff_ffff);
|
||||
peripherals.NVIC.icpr[0].write(0xffff_ffff);
|
||||
peripherals.NVIC.icer[1].write(0xffff_ffff);
|
||||
peripherals.NVIC.icpr[1].write(0xffff_ffff);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
rprintln!("Boot jump to {:08X}", address);
|
||||
let address_pointer = address as *const u32;
|
||||
// https://docs.rs/cortex-m/0.7.2/cortex_m/asm/fn.bootload.html
|
||||
unsafe { asm::bootload(address_pointer) };
|
||||
}
|
||||
}
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
#[cfg(debug_assertions)]
|
||||
rtt_init_print!();
|
||||
#[cfg(debug_assertions)]
|
||||
rprintln!("Starting bootloader");
|
||||
let partition_a = BootPartition {
|
||||
firmware_address: 0x20000,
|
||||
metadata_address: 0x4000,
|
||||
};
|
||||
let partition_b = BootPartition {
|
||||
firmware_address: 0x60000,
|
||||
metadata_address: 0x5000,
|
||||
};
|
||||
#[cfg(debug_assertions)]
|
||||
rprintln!("Reading partition A");
|
||||
let version_a = partition_a.read_version();
|
||||
#[cfg(debug_assertions)]
|
||||
rprintln!("Reading partition B");
|
||||
let version_b = partition_b.read_version();
|
||||
|
||||
match (version_a, version_b) {
|
||||
(Ok(t1), Ok(t2)) => {
|
||||
if t1 >= t2 {
|
||||
partition_a.boot()
|
||||
} else {
|
||||
partition_b.boot()
|
||||
}
|
||||
}
|
||||
(Ok(_), Err(_)) => partition_a.boot(),
|
||||
(Err(_), Ok(_)) => partition_b.boot(),
|
||||
(Err(_), Err(_)) => panic!(),
|
||||
}
|
||||
}
|
||||
137
bootloader/src/registers.rs
Normal file
137
bootloader/src/registers.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright 2020-2022 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::bitfields::{
|
||||
Busy, CryptoMode, HashControl, Interrupts, LliWord1, PaddingConfig, RgfEndianness, Task,
|
||||
};
|
||||
use tock_registers::register_structs;
|
||||
use tock_registers::registers::{ReadOnly, ReadWrite, WriteOnly};
|
||||
|
||||
register_structs! {
|
||||
pub CryptoCellControlRegisters {
|
||||
/// Defines the cryptographic flow
|
||||
(0x0000 => pub crypto_ctl: WriteOnly<u32, CryptoMode::Register>),
|
||||
(0x0004 => _reserved0),
|
||||
/// This register is set whent the cryptographic core is busy
|
||||
(0x0010 => pub crypto_busy: ReadOnly<u32, Busy::Register>),
|
||||
(0x0014 => _reserved1),
|
||||
/// This register is set when the Hash engine is busy
|
||||
(0x001C => pub hash_busy: ReadOnly<u32, Busy::Register>),
|
||||
(0x0020 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
pub CryptoCellDinRegisters {
|
||||
(0x0000 => _reserved0),
|
||||
/// Indicates whether memoty (AXI) source DMA (DIN) is busy
|
||||
(0x0020 => pub mem_dma_busy: ReadOnly<u32, Busy::Register>),
|
||||
(0x0024 => _reserved1),
|
||||
/// This register is used in direct LLI mode - holds the location of the data source
|
||||
/// in the memory (AXI)
|
||||
(0x0028 => pub src_lli_word0: WriteOnly<u32>),
|
||||
/// This register is used in direct LLI mode - holds the number of bytes to be read
|
||||
/// from the memory (AXI).
|
||||
/// Writing to this register triggers the DMA.
|
||||
(0x002C => pub src_lli_word1: WriteOnly<u32, LliWord1::Register>),
|
||||
(0x0030 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
pub CryptoCellHashRegisters {
|
||||
/// Write initial hash value or read final hash value
|
||||
(0x0000 => pub hash: [ReadWrite<u32>; 9]),
|
||||
(0x0024 => _reserved0),
|
||||
/// HW padding automatically activated by engine.
|
||||
/// For the special case of ZERO bytes data vector this register should not be used! instead use HASH_PAD_CFG
|
||||
(0x0044 => pub auto_hw_padding: WriteOnly<u32, Task::Register>),
|
||||
(0x0048 => _reserved1),
|
||||
/// Selects which HASH mode to run
|
||||
(0x0180 => pub control: ReadWrite<u32, HashControl::Register>),
|
||||
/// This register enables the hash hw padding.
|
||||
(0x0184 => pub padding: ReadWrite<u32, Task::Register>),
|
||||
/// HASH_PAD_CFG Register.
|
||||
(0x0188 => pub pad_config: ReadWrite<u32, PaddingConfig::Register>),
|
||||
/// This register hold the length of current hash operation
|
||||
(0x018C => pub hash_len_lsb: ReadWrite<u32>),
|
||||
/// This register hold the length of current hash operation
|
||||
(0x0190 => pub hash_len_msb: ReadWrite<u32>),
|
||||
(0x0194 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
pub CryptoCellHostRgfRegisters {
|
||||
/// The Interrupt Request register.
|
||||
/// Each bit of this register holds the interrupt status of a single interrupt source.
|
||||
(0x0000 => pub interrupts: ReadOnly<u32, Interrupts::Register>),
|
||||
/// The Interrupt Mask register. Each bit of this register holds the mask of a single
|
||||
/// interrupt source.
|
||||
(0x0004 => pub interrupt_mask: ReadWrite<u32, Interrupts::Register>),
|
||||
/// Interrupt Clear Register
|
||||
(0x0008 => pub interrupt_clear: WriteOnly<u32, Interrupts::Register>),
|
||||
/// This register defines the endianness of the Host-accessible registers.
|
||||
(0x000C => pub endian: ReadWrite<u32, RgfEndianness::Register>),
|
||||
(0x0010 => _reserved0),
|
||||
/// This register holds the CryptoCell product signature.
|
||||
(0x0024 => pub signature: ReadOnly<u32>),
|
||||
(0x0028 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
pub CryptoCellMiscRegisters {
|
||||
(0x0000 => _reserved0),
|
||||
/// The HASH clock enable register
|
||||
(0x0018 => pub hash_clk_enable: ReadWrite<u32, Task::Register>),
|
||||
/// The PKA clock enable register
|
||||
(0x001C => _reserved1),
|
||||
/// The DMA clock enable register
|
||||
(0x0020 => pub dma_clk_enable: ReadWrite<u32, Task::Register>),
|
||||
/// the CryptoCell clocks' status register
|
||||
(0x0024 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
pub NordicCC310Registers {
|
||||
(0x0000 => pub enable: ReadWrite<u32, Task::Register>),
|
||||
(0x0004 => @END),
|
||||
},
|
||||
|
||||
pub CryptoCellRegisters {
|
||||
(0x0000 => _reserved0),
|
||||
/// HASH registers
|
||||
/// - Base address: 0x0640
|
||||
(0x0640 => pub hash: CryptoCellHashRegisters),
|
||||
(0x07D4 => _reserved1),
|
||||
/// Misc registers
|
||||
/// - Base address: 0x0800
|
||||
(0x0800 => pub misc: CryptoCellMiscRegisters),
|
||||
(0x0824 => _reserved2),
|
||||
/// CryptoCell control registers
|
||||
/// - Base address: 0x0900
|
||||
(0x0900 => pub ctrl: CryptoCellControlRegisters),
|
||||
(0x0920 => _reserved3),
|
||||
/// HOST_RGF registers
|
||||
/// - Base address: 0x0A00
|
||||
(0x0A00 => pub host_rgf: CryptoCellHostRgfRegisters),
|
||||
(0x0A28 => _reserved4),
|
||||
/// DIN registers
|
||||
/// - Base address: 0x0C00
|
||||
(0x0C00 => pub din: CryptoCellDinRegisters),
|
||||
(0x0C30 => @END),
|
||||
}
|
||||
}
|
||||
46
bootloader/src/static_ref.rs
Normal file
46
bootloader/src/static_ref.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
//! Wrapper type for safe pointers to static memory.
|
||||
//!
|
||||
//! Imported from:
|
||||
//! https://github.com/tock/tock/blob/master/kernel/src/utilities/static_ref.rs
|
||||
|
||||
use core::ops::Deref;
|
||||
|
||||
/// A pointer to statically allocated mutable data such as memory mapped I/O
|
||||
/// registers.
|
||||
///
|
||||
/// This is a simple wrapper around a raw pointer that encapsulates an unsafe
|
||||
/// dereference in a safe manner. It serve the role of creating a `&'static T`
|
||||
/// given a raw address and acts similarly to `extern` definitions, except
|
||||
/// `StaticRef` is subject to module and crate boundaries, while `extern`
|
||||
/// definitions can be imported anywhere.
|
||||
#[derive(Debug)]
|
||||
pub struct StaticRef<T> {
|
||||
ptr: *const T,
|
||||
}
|
||||
|
||||
impl<T> StaticRef<T> {
|
||||
/// Create a new `StaticRef` from a raw pointer
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// Callers must pass in a reference to statically allocated memory which
|
||||
/// does not overlap with other values.
|
||||
pub const unsafe fn new(ptr: *const T) -> StaticRef<T> {
|
||||
StaticRef { ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for StaticRef<T> {
|
||||
fn clone(&self) -> Self {
|
||||
StaticRef { ptr: self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for StaticRef<T> {}
|
||||
|
||||
impl<T> Deref for StaticRef<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.ptr }
|
||||
}
|
||||
}
|
||||
32
build.rs
32
build.rs
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019 Google LLC
|
||||
// Copyright 2019-2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,24 +12,46 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::env;
|
||||
extern crate alloc;
|
||||
|
||||
use openssl::{bn, ec, nid};
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
use std::{env, fs};
|
||||
use uuid::Uuid;
|
||||
|
||||
fn main() {
|
||||
const UPGRADE_FILE: &str = "crypto_data/opensk_upgrade_pub.pem";
|
||||
println!("cargo:rerun-if-changed=crypto_data/aaguid.txt");
|
||||
println!("cargo:rerun-if-changed={UPGRADE_FILE}");
|
||||
println!("cargo:rerun-if-changed=layout.ld");
|
||||
println!("cargo:rerun-if-changed=nrf52840_layout.ld");
|
||||
println!("cargo:rerun-if-changed=nrf52840_layout_a.ld");
|
||||
println!("cargo:rerun-if-changed=nrf52840_layout_b.ld");
|
||||
|
||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||
let aaguid_bin_path = Path::new(&out_dir).join("opensk_aaguid.bin");
|
||||
|
||||
let mut aaguid_bin_file = File::create(&aaguid_bin_path).unwrap();
|
||||
let mut aaguid_bin_file = File::create(aaguid_bin_path).unwrap();
|
||||
let mut aaguid_txt_file = File::open("crypto_data/aaguid.txt").unwrap();
|
||||
let mut content = String::new();
|
||||
aaguid_txt_file.read_to_string(&mut content).unwrap();
|
||||
content.truncate(36);
|
||||
let aaguid = Uuid::parse_str(&content).unwrap();
|
||||
aaguid_bin_file.write_all(aaguid.as_bytes()).unwrap();
|
||||
|
||||
// COSE encoding the public key, then write it out.
|
||||
let pem_bytes = fs::read(UPGRADE_FILE).unwrap();
|
||||
let ec_key = ec::EcKey::public_key_from_pem(&pem_bytes).ok().unwrap();
|
||||
let group = ec::EcGroup::from_curve_name(nid::Nid::X9_62_PRIME256V1).unwrap();
|
||||
let conversion_form = ec::PointConversionForm::UNCOMPRESSED;
|
||||
let mut ctx = bn::BigNumContext::new().unwrap();
|
||||
let raw_bytes = ec_key
|
||||
.public_key()
|
||||
.to_bytes(&group, conversion_form, &mut ctx)
|
||||
.unwrap();
|
||||
let upgrade_pubkey_path = Path::new(&out_dir).join("opensk_upgrade_pubkey.bin");
|
||||
let mut upgrade_pub_bin_file = File::create(upgrade_pubkey_path).unwrap();
|
||||
upgrade_pub_bin_file.write_all(&raw_bytes).unwrap();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@ customize it.
|
||||
|
||||
### Flashing using DFU (preferred method)
|
||||
|
||||
You need `nrfutil` version 6. The [install manual](../install.md) has
|
||||
setup instructions.
|
||||
|
||||
To flash the firmware, run:
|
||||
|
||||
```shell
|
||||
@@ -59,14 +62,48 @@ Follow these steps:
|
||||
|
||||

|
||||
|
||||
1. Depending on the programmer you're using, you may have to adapt the next
|
||||
command line. Run our script for compiling/flashing Tock OS on your device:
|
||||
#### JLink
|
||||
|
||||
Run our script for compiling/flashing Tock OS on your device:
|
||||
|
||||
```shell
|
||||
$ ./deploy.py --board=nrf52840_dongle --programmer=jlink
|
||||
```
|
||||
|
||||
#### OpenOCD
|
||||
|
||||
1. Create your openocd config, named `nordic_nrf52840_dongle.cfg` in the
|
||||
appropriate location:
|
||||
```shell
|
||||
$ ./deploy.py --board=nrf52840_dongle --programmer=jlink
|
||||
mkdir -p ${HOME}/.openocd/board
|
||||
touch ${HOME}/.openocd/board/nordic_nrf52840_dongle.cfg
|
||||
```
|
||||
|
||||
Paste the following st-link example and edit the specific setup to your needs:
|
||||
```
|
||||
# Specific setup
|
||||
source [find interface/stlink-dap.cfg]
|
||||
transport select dapdirect_swd
|
||||
|
||||
# The rest should be kept the same
|
||||
set CHIPNAME nrf52840
|
||||
source [find target/nrf52.cfg]
|
||||
```
|
||||
|
||||
1. Remove the programming cable and the USB-A extension cable.
|
||||
1. Test your config:
|
||||
|
||||
```shell
|
||||
openocd -f board/nordic_nrf52840_dongle.cfg
|
||||
```
|
||||
|
||||
1. Run the deploy script with the appropriate options, i.e.:
|
||||
|
||||
```shell
|
||||
./deploy.py --board=nrf52840_dongle --opensk --programmer=openocd
|
||||
```
|
||||
|
||||
|
||||
Finally, remove the programming cable and the USB-A extension cable.
|
||||
|
||||
### Buttons and LEDs
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ After general setup, you still need these steps:
|
||||
1. Run the script:
|
||||
|
||||
```shell
|
||||
python3 uf2conv.py -c -f 0xada52840 -o target/opensk.uf2 target/nrf52840_mdk_dfu_merged.hex
|
||||
py_virtual_env/bin/python3 uf2conv.py -c -f 0xada52840 -o target/opensk.uf2 target/nrf52840_mdk_dfu_merged.hex
|
||||
```
|
||||
|
||||
1. Boot into DFU mode. Keep the user button pressed on your hardware while
|
||||
|
||||
@@ -14,7 +14,7 @@ is the easiest and most convenient. You can flash OpenSK with these steps:
|
||||
1. Run our script for compiling/flashing Tock OS and OpenSK on your device:
|
||||
|
||||
```shell
|
||||
./deploy.py --board=nrf52840dk --opensk
|
||||
./deploy.py --board=nrf52840dk_opensk --opensk
|
||||
```
|
||||
|
||||
1. Connect a micro USB cable to the device USB port.
|
||||
@@ -48,3 +48,38 @@ There are 3 switches that need to be in the correct position:
|
||||
* Power (bottom left): On
|
||||
* nRF power source (center left): VDD
|
||||
* SW6 (top right): DEFAULT
|
||||
|
||||
### Upgradability
|
||||
|
||||
There are variants of the board that introduce A/B partitions for upgrading the
|
||||
firmware. You can bootstrap an upgradable board using one of the two commands:
|
||||
|
||||
```shell
|
||||
./deploy.py --board=nrf52840dk_opensk_a --opensk --version=0
|
||||
./deploy.py --board=nrf52840dk_opensk_b --opensk --version=0
|
||||
```
|
||||
|
||||
Afterwards, you can upgrade the other partition with
|
||||
|
||||
```shell
|
||||
# Board A -> B
|
||||
./deploy.py --board=nrf52840dk_opensk_b --opensk --programmer=none --version=1
|
||||
py_virtual_env/bin/python3 -m tools.deploy_partition --board=nrf52840dk_opensk_b --version=1
|
||||
# Board B -> A
|
||||
./deploy.py --board=nrf52840dk_opensk_a --opensk --programmer=none --version=1
|
||||
py_virtual_env/bin/python3 -m tools.deploy_partition --board=nrf52840dk_opensk_a --version=1
|
||||
```
|
||||
|
||||
respectively. You can only upgrade the partition that is not currently running,
|
||||
otherwise your deploy attempts will fail. You can call `deploy_partition` after
|
||||
you locked down your device, to deploy changes to your development board.
|
||||
Upgrades only apply after a reboot.
|
||||
|
||||
If you want to use Vendor HID, add the `--vendor-hid` flag to all calls,
|
||||
for example:
|
||||
|
||||
```shell
|
||||
./deploy.py --board=nrf52840dk_opensk_a --opensk --version=0 --vendor-hid
|
||||
./deploy.py --board=nrf52840dk_opensk_b --opensk --programmer=none --version=1 --vendor-hid
|
||||
py_virtual_env/bin/python3 -m tools.deploy_partition --board=nrf52840dk_opensk_b --version=1 --vendor-hid
|
||||
```
|
||||
|
||||
@@ -7,16 +7,18 @@
|
||||
All the generated certificates and private keys are stored in the directory
|
||||
`crypto_data/`. The expected content after running our `setup.sh` script is:
|
||||
|
||||
File | Purpose
|
||||
------------------------ | --------------------------------------------------------
|
||||
`aaguid.txt` | Text file containaing the AAGUID value
|
||||
`opensk_ca.csr` | Certificate sign request for the Root CA
|
||||
`opensk_ca.key` | ECC secp256r1 private key used for the Root CA
|
||||
`opensk_ca.pem` | PEM encoded certificate of the Root CA
|
||||
`opensk_ca.srl` | File generated by OpenSSL
|
||||
`opensk_cert.csr` | Certificate sign request for the attestation certificate
|
||||
`opensk_cert.pem` | PEM encoded certificate used for the authenticator
|
||||
`opensk.key` | ECC secp256r1 private key used for the autenticator
|
||||
| File | Purpose |
|
||||
| ------------------------ | ----------------------------------------------- |
|
||||
| `aaguid.txt` | Text file containaing the AAGUID value |
|
||||
| `opensk_ca.csr` | Certificate sign request for the Root CA |
|
||||
| `opensk_ca.key` | ECC secp256r1 private key used for the Root CA |
|
||||
| `opensk_ca.pem` | PEM encoded certificate of the Root CA |
|
||||
| `opensk_ca.srl` | File generated by OpenSSL |
|
||||
| `opensk_cert.csr` | CSR for attestation certificate |
|
||||
| `opensk_cert.pem` | PEM encoded certificate for the authenticator |
|
||||
| `opensk.key` | ECC secp256r1 private key for the autenticator |
|
||||
| `opensk_upgrade.key` | Private key for signing upgrades through CTAP |
|
||||
| `opensk_upgrade_pub.pem` | Public key for verifying upgrades |
|
||||
|
||||
If you want to use your own attestation certificate and private key,
|
||||
replace the `opensk_cert.pem` and `opensk.key` files. The script at
|
||||
@@ -48,31 +50,46 @@ carefully if you want to take this privacy risk.
|
||||
If you build your own security key, depending on the hardware you use, there are
|
||||
a few things you can personalize:
|
||||
|
||||
1. If you have multiple buttons, choose the buttons responsible for user
|
||||
presence in `main.rs`.
|
||||
2. Decide whether you want to use batch attestation. There is a boolean flag in
|
||||
`ctap/mod.rs`. It is mandatory for U2F, and you can create your own
|
||||
self-signed certificate. The flag is used for FIDO2 and has some privacy
|
||||
implications. Please check
|
||||
[WebAuthn](https://www.w3.org/TR/webauthn/#attestation) for more
|
||||
information.
|
||||
3. Decide whether you want to use signature counters. Currently, only global
|
||||
signature counters are implemented, as they are the default option for U2F.
|
||||
The flag in `ctap/mod.rs` only turns them off for FIDO2. The most privacy
|
||||
preserving solution is individual or no signature counters. Again, please
|
||||
check [WebAuthn](https://www.w3.org/TR/webauthn/#signature-counter) for
|
||||
documentation.
|
||||
4. Depending on your available flash storage, choose an appropriate maximum
|
||||
number of supported resident keys and number of pages in
|
||||
`ctap/storage.rs`.
|
||||
5. Change the default level for the credProtect extension in `ctap/mod.rs`.
|
||||
When changing the default, resident credentials become undiscoverable without
|
||||
user verification. This helps privacy, but can make usage less comfortable
|
||||
for credentials that need less protection.
|
||||
6. Increase the default minimum length for PINs in `ctap/storage.rs`.
|
||||
The current minimum is 4. Values from 4 to 63 are allowed. Requiring longer
|
||||
PINs can help establish trust between users and relying parties. It makes
|
||||
user verification harder to break, but less convenient.
|
||||
NIST recommends at least 6-digit PINs in section 5.1.9.1 of their
|
||||
[Digital Identity Guidelines](https://pages.nist.gov/800-63-3/sp800-63b.html).
|
||||
You can add relying parties to the list of readers of the minimum PIN length.
|
||||
1. If you have multiple buttons, choose the buttons responsible for user
|
||||
presence in `src/main.rs`.
|
||||
1. If you have colored LEDs, like different blinking patterns and want to play
|
||||
around with the code in `src/main.rs` more, take a look at e.g. `wink_leds`.
|
||||
1. You find more options and documentation in `src/ctap/customization.rs`,
|
||||
including:
|
||||
* The default level for the credProtect extension.
|
||||
* The default minimum PIN length, and what relying parties can set it.
|
||||
* Whether you want to enforce alwaysUv.
|
||||
* Settings for enterprise attestation.
|
||||
* The maximum PIN retries.
|
||||
* Whether you want to use batch attestation.
|
||||
* Whether you want to use signature counters.
|
||||
* Various constants to adapt to different hardware.
|
||||
|
||||
### Testing and Fuzzing
|
||||
|
||||
You might want to test your changes before deploying them. To run unit tests,
|
||||
make sure that at least the `std` feature is included, e.g.:
|
||||
|
||||
```shell
|
||||
cargo test --features=std,with_ctap1
|
||||
```
|
||||
|
||||
Alternatively, you can simply call our test script to also test all libraries,
|
||||
run clippy, check formatting and more:
|
||||
|
||||
```shell
|
||||
./run_desktop_tests.sh
|
||||
```
|
||||
|
||||
OpenSK is fuzzed with the [OSS-Fuzz](https://github.com/google/oss-fuzz)
|
||||
project. You can also run fuzzing locally. First install:
|
||||
|
||||
```shell
|
||||
./fuzzing_setup.sh
|
||||
```
|
||||
|
||||
Then choose a fuzz target from `fuzz/fuzz_targets/`, e.g.:
|
||||
|
||||
```shell
|
||||
cargo fuzz run fuzz_target_process_ctap1
|
||||
```
|
||||
|
||||
@@ -59,15 +59,15 @@ JLinkRTTClient
|
||||
You can enhance the debug output by adding flags to the deploy command (see
|
||||
below for details):
|
||||
|
||||
* `--debug`: more debug messages
|
||||
* `--panic-console`: add panic messages
|
||||
* `--debug-allocations`: print information about the used heap
|
||||
* `--debug`: more debug messages
|
||||
* `--panic-console`: add panic messages
|
||||
* `--debug-allocations`: print information about the used heap
|
||||
|
||||
Adding debugging to your firmware increases resource usage, including
|
||||
|
||||
* USB communication speed
|
||||
* RAM usage
|
||||
* binary size
|
||||
* USB communication speed
|
||||
* RAM usage
|
||||
* binary size
|
||||
|
||||
Depending on your choice of board, you may have to increase the available stack
|
||||
for kernel or app, or disable features so that the binary fits the flash. Also
|
||||
@@ -83,7 +83,7 @@ driver, before faulting the app, you can use the `--panic-console` flag of the
|
||||
|
||||
```shell
|
||||
# Example on Nordic nRF52840-DK board
|
||||
./deploy.py --board=nrf52840dk --opensk --panic-console
|
||||
./deploy.py --board=nrf52840dk_opensk --opensk --panic-console
|
||||
```
|
||||
|
||||
### Memory allocations
|
||||
@@ -107,31 +107,3 @@ alloc[256, 1] = 0x2002401c (2 ptrs, 384 bytes)
|
||||
# After this operation, 1 pointers are allocated, totalling 512 bytes.
|
||||
dealloc[64, 1] = 0x2002410c (1 ptrs, 512 bytes)
|
||||
```
|
||||
|
||||
A tool is provided to analyze such reports, in `tools/heapviz`. This tool
|
||||
parses the console output, identifies the lines corresponding to (de)allocation
|
||||
operations, and first computes some statistics:
|
||||
|
||||
* Address range used by the heap over this run of the program,
|
||||
* Peak heap usage (how many useful bytes are allocated),
|
||||
* Peak heap consumption (how many bytes are used by the heap, including
|
||||
unavailable bytes between allocated blocks, due to alignment constraints and
|
||||
memory fragmentation),
|
||||
* Fragmentation overhead (difference between heap consumption and usage).
|
||||
|
||||
Then, the `heapviz` tool displays an animated "movie" of the allocated bytes in
|
||||
heap memory. Each frame in this "movie" shows bytes that are currently
|
||||
allocated, that were allocated but are now freed, and that have never been
|
||||
allocated. A new frame is generated for each (de)allocation operation. This tool
|
||||
uses the `ncurses` library, that you may have to install beforehand.
|
||||
|
||||
You can control the tool with the following parameters:
|
||||
|
||||
* `--logfile` (required) to provide the file which contains the console output
|
||||
to parse,
|
||||
* `--fps` (optional) to customize the number of frames per second in the movie
|
||||
animation.
|
||||
|
||||
```shell
|
||||
cargo run --manifest-path tools/heapviz/Cargo.toml -- --logfile console.log --fps 50
|
||||
```
|
||||
|
||||
137
docs/install.md
137
docs/install.md
@@ -8,26 +8,27 @@ This document lists required steps to start build your own OpenSK.
|
||||
|
||||
OpenSK supports different ways to flash your board:
|
||||
|
||||
* [Segger J-Link](https://www.segger.com/products/debug-probes/j-link/)
|
||||
(default method).
|
||||
* [OpenOCD](http://openocd.org/).
|
||||
* [pyOCD](https://pypi.org/project/pyocd/).
|
||||
* [nrfutil](https://pypi.org/project/nrfutil/) for the USB dongle boards that
|
||||
support it, which allows you to directly flash a working board over USB
|
||||
without additional hardware.
|
||||
* [Segger J-Link](https://www.segger.com/products/debug-probes/j-link/)
|
||||
(default method).
|
||||
* [OpenOCD](http://openocd.org/).
|
||||
* [pyOCD](https://pypi.org/project/pyocd/).
|
||||
* [nrfutil](https://pypi.org/project/nrfutil/) for the USB dongle boards that
|
||||
support it, which allows you to directly flash a working board over USB
|
||||
without additional hardware.
|
||||
|
||||
### Software requirements
|
||||
|
||||
In order to compile and flash a working OpenSK firmware, you will need the
|
||||
following:
|
||||
|
||||
* rustup (can be installed with [Rustup](https://rustup.rs/))
|
||||
* python3 and pip (can be installed with the `python3-pip` package on Debian)
|
||||
* the OpenSSL command line tool (can be installed and configured with the
|
||||
`libssl-dev` and `pkg-config` packages on Debian)
|
||||
* `nrfutil` (can be installed using `pip3 install nrfutil`) if you want to flash
|
||||
a device with DFU
|
||||
* `uuid-runtime` if you are missing the `uuidgen` command.
|
||||
* rustup (can be installed with [Rustup](https://rustup.rs/))
|
||||
* python3 and pip (can be installed with the `python3-pip` package on Debian)
|
||||
* the OpenSSL command line tool (can be installed and configured with the
|
||||
`libssl-dev` and `pkg-config` packages on Debian)
|
||||
* `nrfutil` (pip package of the same name), if you want to flash
|
||||
a device with DFU. Read the disclaimer below.
|
||||
* `uuid-runtime` if you are missing the `uuidgen` command.
|
||||
* `llvm` and `gcc-arm-none-eabi` if you want to use the upgradability feature.
|
||||
|
||||
The proprietary software to use the default programmer can be found on the
|
||||
[Segger website](https://www.segger.com/downloads/jlink). Please follow their
|
||||
@@ -36,35 +37,73 @@ instructions to appropriate binaries for your system.
|
||||
The scripts provided in this project have been tested under Linux and OS X. We
|
||||
haven't tested them on Windows and other platforms.
|
||||
|
||||
You need `nrfutil` version 6, if you want to flash over DFU.
|
||||
The tool doesn't support Python newer than 3.10. Therefore, we don't officially
|
||||
support DFU for other versions. If you want to try regardless,
|
||||
[Nordic's new tool](https://www.nordicsemi.com/Products/Development-tools/nrf-util)
|
||||
might work for you.
|
||||
|
||||
### Compiling the firmware
|
||||
|
||||
If you are switching branches or used an old version of OpenSK before, we have
|
||||
tools to help you migrate to our develop branch. You find more information on
|
||||
how to update your setup or reset your storage in its
|
||||
[install instructions](https://github.com/google/OpenSK/blob/develop/docs/install.md).
|
||||
If this is your first time installing OpenSK, please skip directly to
|
||||
[Initial setup](#initial-setup). Else, see
|
||||
[Updating your setup](#updating-your-setup) below.
|
||||
|
||||
To clone and setup the repository for the stable branch, run the following
|
||||
#### Updating your setup
|
||||
|
||||
Depending on the difference to your last state, you may need some of the
|
||||
following steps:
|
||||
|
||||
* If you are not just testing minor changes, reset and redo the setup. This
|
||||
will delete all uncommited changes.
|
||||
|
||||
```shell
|
||||
./reset.sh
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
* Flash your board according to the [instructions below](#flashing-a-firmware).
|
||||
|
||||
If you come from an OpenSK version before the 2.0 certified one, your credential
|
||||
storage is not backwards compatible and you have to reset it. :warning: You will
|
||||
lose logins to all websites that you registered with OpenSK. To erase your
|
||||
persistent storage, run the deploy script twice: Once with the application
|
||||
parameter `--erase_storage`, and once with `--opensk` as usual.
|
||||
|
||||
This reset also clears the certificate. For a privacy discussion, see the
|
||||
[certificate section in Customization](customization.md#Certificate-considerations).
|
||||
If you want to reinstall it, you also need to rerun:
|
||||
|
||||
```shell
|
||||
./tools/configure.py \
|
||||
--certificate=crypto_data/opensk_cert.pem \
|
||||
--private-key=crypto_data/opensk.key
|
||||
```
|
||||
|
||||
#### Initial setup
|
||||
|
||||
To clone and setup the repository for the develop branch, run the following
|
||||
commands:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/google/OpenSK.git
|
||||
git clone -b develop https://github.com/google/OpenSK.git
|
||||
cd OpenSK
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
The setup script performs the following steps:
|
||||
|
||||
1. Make sure that the git submodules are checked out.
|
||||
1. Make sure that the git submodules are checked out.
|
||||
|
||||
1. Apply our patches that haven't yet been merged upstream to both
|
||||
[Tock](https://github.com/tock/tock) and
|
||||
[libtock-rs](https://github.com/tock/libtock-rs).
|
||||
1. Apply our patches that haven't yet been merged upstream to both
|
||||
[Tock](https://github.com/tock/tock) and
|
||||
[libtock-rs](https://github.com/tock/libtock-rs).
|
||||
|
||||
1. Generate crypto material, see [Customization](customization.md) for details.
|
||||
1. Generate crypto material, see [Customization](customization.md) for details.
|
||||
|
||||
1. Install the correct Rust toolchain for ARM devices.
|
||||
1. Install the correct Rust toolchain for ARM devices.
|
||||
|
||||
1. Install [tockloader](https://github.com/tock/tockloader).
|
||||
1. Install [tockloader](https://github.com/tock/tockloader).
|
||||
|
||||
Additionally on Linux, you need to install a `udev` rule file to allow non-root
|
||||
users to interact with OpenSK devices. To install it, execute:
|
||||
@@ -93,25 +132,47 @@ for understand privacy tradeoffs.
|
||||
|
||||
From here on, please follow the instructions for your hardware:
|
||||
|
||||
* [Nordic nRF52840-DK](boards/nrf52840dk.md)
|
||||
* [Nordic nRF52840 Dongle](boards/nrf52840_dongle.md)
|
||||
* [Makerdiary nRF52840-MDK USB dongle](boards/nrf52840_mdk.md)
|
||||
* [Feitian OpenSK dongle](boards/nrf52840_feitian.md)
|
||||
* [Nordic nRF52840-DK](boards/nrf52840dk.md)
|
||||
* [Nordic nRF52840 Dongle](boards/nrf52840_dongle.md)
|
||||
* [Makerdiary nRF52840-MDK USB dongle](boards/nrf52840_mdk.md)
|
||||
* [Feitian OpenSK dongle](boards/nrf52840_feitian.md)
|
||||
|
||||
### Advanced installation
|
||||
|
||||
We recommend that you flash your development board with JTAG and dongles with
|
||||
DFU, as described in the [board documentation](#Flashing-a-firmware) linked
|
||||
DFU, as described in the [board documentation](#flashing-a-firmware) linked
|
||||
above. However, we support other programmers:
|
||||
|
||||
* OpenOCD: `./deploy.py --board=nrf52840_dongle --opensk --programmer=openocd`
|
||||
* pyOCD: `./deploy.py --board=nrf52840_dongle --opensk --programmer=pyocd`
|
||||
* Custom: `./deploy.py --board=nrf52840_dongle --opensk --programmer=none`.
|
||||
In this case, an IntelHex file will be created and how to program a board is
|
||||
left to the user.
|
||||
* OpenOCD: `./deploy.py --board=nrf52840_dongle_opensk --opensk
|
||||
--programmer=openocd`
|
||||
* pyOCD: `./deploy.py --board=nrf52840_dongle_opensk --opensk
|
||||
--programmer=pyocd`
|
||||
* Custom: `./deploy.py --board=nrf52840_dongle_opensk --opensk
|
||||
--programmer=none`. In this case, an IntelHex file will be created and how
|
||||
to program a board is left to the user.
|
||||
|
||||
If your board is already flashed with Tock OS, you may skip installing it:
|
||||
`./deploy.py --board=nrf52840dk --opensk --no-tockos`
|
||||
`./deploy.py --board=nrf52840dk_opensk --opensk --no-tockos`
|
||||
|
||||
For more options, we invite you to read the help of our `deploy.py` script by
|
||||
running `./deploy.py --help`.
|
||||
|
||||
### Upgradability
|
||||
|
||||
We experiment with a new CTAP command to allow upgrading your device without
|
||||
access to its debugging port. For that purpose, the flash storage is split into
|
||||
4 parts:
|
||||
|
||||
* the bootloader to decide with partition to boot
|
||||
* firmware partition A
|
||||
* firmware partition B
|
||||
* the persistent storage for credentials
|
||||
|
||||
The storage is backward compatible to non-upgradable boards. Deploying an
|
||||
upgradable board automatically installs the bootloader. Please keep in mind that
|
||||
you have to safely store your private signing key for upgrades if you want to
|
||||
use this feature. For more information on the cryptographic material, see
|
||||
[Customization](customization.md).
|
||||
|
||||
So far, upgradability is only supported for the development board. See the
|
||||
instructions on the [board specific page](boards/nrf52840dk.md).
|
||||
|
||||
@@ -12,22 +12,32 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate lang_items;
|
||||
|
||||
use libtock_drivers::console::{Console, BUFFER_SIZE};
|
||||
use libtock_console::Console;
|
||||
use libtock_drivers::result::FlexUnwrap;
|
||||
use libtock_runtime::{set_main, stack_size, TockSyscalls};
|
||||
|
||||
stack_size! {0x800}
|
||||
set_main! {main}
|
||||
|
||||
type Syscalls = TockSyscalls;
|
||||
|
||||
fn main() {
|
||||
// Write messages of length up to the console driver's buffer size.
|
||||
let mut buf = [0; BUFFER_SIZE];
|
||||
let mut buf = [0; 1024];
|
||||
loop {
|
||||
for i in 1..buf.len() {
|
||||
for byte in buf.iter_mut().take(i) {
|
||||
*byte = b'0' + ((i % 10) as u8);
|
||||
}
|
||||
buf[i] = b'\n';
|
||||
Console::write_unbuffered(&mut buf[..(i + 1)]);
|
||||
Console::<Syscalls>::write(&buf[..(i + 1)])
|
||||
.map_err(|e| e.into())
|
||||
.flex_unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
@@ -20,83 +21,72 @@ extern crate lang_items;
|
||||
use alloc::format;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::Write;
|
||||
use crypto::{
|
||||
aes256, cbc, ecdsa, rng256, sha256, Decrypt16BytesBlock, Encrypt16BytesBlock, Hash256,
|
||||
};
|
||||
use libtock_drivers::console::Console;
|
||||
use core::hint::black_box;
|
||||
use ctap2::env::tock::{TockEnv, TockRng};
|
||||
use libtock_console::{Console, ConsoleWriter};
|
||||
use libtock_drivers::result::FlexUnwrap;
|
||||
use libtock_drivers::timer;
|
||||
use libtock_drivers::timer::Timer;
|
||||
use libtock_drivers::timer::Timestamp;
|
||||
use libtock_drivers::timer::{Timer, Timestamp};
|
||||
use libtock_runtime::{set_main, stack_size, TockSyscalls};
|
||||
use opensk::api::crypto::aes256::Aes256;
|
||||
use opensk::api::crypto::ecdsa::SecretKey as _;
|
||||
use opensk::api::crypto::sha256::Sha256;
|
||||
use opensk::env::{AesKey, EcdsaSk, Sha};
|
||||
|
||||
stack_size! {0x2000}
|
||||
set_main! {main}
|
||||
|
||||
type Syscalls = TockSyscalls;
|
||||
|
||||
fn main() {
|
||||
let mut console = Console::new();
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
// Setup the timer with a dummy callback (we only care about reading the current time, but the
|
||||
// API forces us to set an alarm callback too).
|
||||
let mut with_callback = timer::with_callback(|_, _| {});
|
||||
let mut with_callback = timer::with_callback(|_| {});
|
||||
let timer = with_callback.init().flex_unwrap();
|
||||
|
||||
let mut rng = rng256::TockRng256 {};
|
||||
let mut rng = TockRng::<Syscalls>::default();
|
||||
|
||||
writeln!(console, "****************************************").unwrap();
|
||||
writeln!(
|
||||
console,
|
||||
"Clock frequency: {} Hz",
|
||||
timer.clock_frequency().hz()
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(console, "Clock frequency: {:?} Hz", timer.clock_frequency()).unwrap();
|
||||
|
||||
// AES
|
||||
bench(&mut console, &timer, "aes256::EncryptionKey::new", || {
|
||||
aes256::EncryptionKey::new(&[0; 32]);
|
||||
bench(&mut console, &timer, "Aes256::new", || {
|
||||
black_box(AesKey::<TockEnv<Syscalls>>::new(&[0; 32]));
|
||||
});
|
||||
let ek = aes256::EncryptionKey::new(&[0; 32]);
|
||||
bench(&mut console, &timer, "aes256::DecryptionKey::new", || {
|
||||
aes256::DecryptionKey::new(&ek);
|
||||
});
|
||||
let dk = aes256::DecryptionKey::new(&ek);
|
||||
let aes_key = AesKey::<TockEnv<Syscalls>>::new(&[0; 32]);
|
||||
|
||||
bench(
|
||||
&mut console,
|
||||
&timer,
|
||||
"aes256::EncryptionKey::encrypt_block",
|
||||
|| {
|
||||
ek.encrypt_block(&mut [0; 16]);
|
||||
},
|
||||
);
|
||||
bench(
|
||||
&mut console,
|
||||
&timer,
|
||||
"aes256::DecryptionKey::decrypt_block",
|
||||
|| {
|
||||
dk.decrypt_block(&mut [0; 16]);
|
||||
},
|
||||
);
|
||||
bench(&mut console, &timer, "Aes256::encrypt_block", || {
|
||||
aes_key.encrypt_block(&mut [0; 16]);
|
||||
});
|
||||
bench(&mut console, &timer, "Aes256::decrypt_block", || {
|
||||
aes_key.decrypt_block(&mut [0; 16]);
|
||||
});
|
||||
|
||||
// CBC
|
||||
let mut blocks = Vec::new();
|
||||
for i in 0..8 {
|
||||
blocks.resize(1 << i, [0; 16]);
|
||||
for i in 0..6 {
|
||||
blocks.resize(1 << (i + 4), 0);
|
||||
bench(
|
||||
&mut console,
|
||||
&timer,
|
||||
&format!("cbc::cbc_encrypt({} bytes)", blocks.len() * 16),
|
||||
&format!("Aes256::encrypt_cbc({} bytes)", blocks.len()),
|
||||
|| {
|
||||
cbc::cbc_encrypt(&ek, [0; 16], &mut blocks);
|
||||
aes_key.encrypt_cbc(&[0; 16], &mut blocks);
|
||||
},
|
||||
);
|
||||
}
|
||||
drop(blocks);
|
||||
|
||||
let mut blocks = Vec::new();
|
||||
for i in 0..8 {
|
||||
blocks.resize(1 << i, [0; 16]);
|
||||
for i in 0..6 {
|
||||
blocks.resize(1 << (i + 4), 0);
|
||||
bench(
|
||||
&mut console,
|
||||
&timer,
|
||||
&format!("cbc::cbc_decrypt({} bytes)", blocks.len() * 16),
|
||||
&format!("Aes256::decrypt_cbc({} bytes)", blocks.len()),
|
||||
|| {
|
||||
cbc::cbc_decrypt(&dk, [0; 16], &mut blocks);
|
||||
aes_key.decrypt_cbc(&[0; 16], &mut blocks);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -104,52 +94,37 @@ fn main() {
|
||||
|
||||
// SHA-256
|
||||
let mut contents = Vec::new();
|
||||
for i in 0..8 {
|
||||
for i in 0..6 {
|
||||
contents.resize(16 << i, 0);
|
||||
bench(
|
||||
&mut console,
|
||||
&timer,
|
||||
&format!("sha256::Sha256::update({} bytes)", contents.len()),
|
||||
&format!("Sha256::digest({} bytes)", contents.len()),
|
||||
|| {
|
||||
let mut sha = sha256::Sha256::new();
|
||||
sha.update(&contents);
|
||||
sha.finalize();
|
||||
Sha::<TockEnv<Syscalls>>::digest(&contents);
|
||||
},
|
||||
);
|
||||
}
|
||||
drop(contents);
|
||||
|
||||
// ECDSA
|
||||
bench(&mut console, &timer, "ecdsa::SecKey::gensk", || {
|
||||
ecdsa::SecKey::gensk(&mut rng);
|
||||
bench(&mut console, &timer, "Ecdsa::SecretKey::random", || {
|
||||
EcdsaSk::<TockEnv<Syscalls>>::random(&mut rng);
|
||||
});
|
||||
let k = ecdsa::SecKey::gensk(&mut rng);
|
||||
bench(&mut console, &timer, "ecdsa::SecKey::genpk", || {
|
||||
k.genpk();
|
||||
let sk = EcdsaSk::<TockEnv<Syscalls>>::random(&mut rng);
|
||||
bench(&mut console, &timer, "Ecdsa::SecretKey::public_key", || {
|
||||
black_box(sk.public_key());
|
||||
});
|
||||
bench(&mut console, &timer, "Ecdsa::SecretKey::sign", || {
|
||||
sk.sign(&[]);
|
||||
});
|
||||
bench(
|
||||
&mut console,
|
||||
&timer,
|
||||
"ecdsa::SecKey::sign_rng::<sha256::Sha256, _>",
|
||||
|| {
|
||||
k.sign_rng::<sha256::Sha256, _>(&[], &mut rng);
|
||||
},
|
||||
);
|
||||
bench(
|
||||
&mut console,
|
||||
&timer,
|
||||
"ecdsa::SecKey::sign_rfc6979::<sha256::Sha256>",
|
||||
|| {
|
||||
k.sign_rfc6979::<sha256::Sha256>(&[]);
|
||||
},
|
||||
);
|
||||
|
||||
writeln!(console, "****************************************").unwrap();
|
||||
writeln!(console, "All the benchmarks are done.\nHave a nice day!").unwrap();
|
||||
writeln!(console, "****************************************").unwrap();
|
||||
}
|
||||
|
||||
fn bench<F>(console: &mut Console, timer: &Timer, title: &str, mut f: F)
|
||||
fn bench<F>(console: &mut ConsoleWriter<Syscalls>, timer: &Timer<Syscalls>, title: &str, mut f: F)
|
||||
where
|
||||
F: FnMut(),
|
||||
{
|
||||
@@ -158,11 +133,13 @@ where
|
||||
writeln!(console, "----------------------------------------").unwrap();
|
||||
let mut count = 1;
|
||||
for _ in 0..30 {
|
||||
let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
|
||||
let start =
|
||||
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
|
||||
for _ in 0..count {
|
||||
f();
|
||||
}
|
||||
let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
|
||||
let end =
|
||||
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
|
||||
let elapsed = (end - start).ms();
|
||||
writeln!(
|
||||
console,
|
||||
@@ -172,7 +149,6 @@ where
|
||||
elapsed / (count as f64)
|
||||
)
|
||||
.unwrap();
|
||||
console.flush();
|
||||
if elapsed > 1000.0 {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -12,16 +12,25 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate lang_items;
|
||||
|
||||
use core::fmt::Write;
|
||||
use ctap2::embedded_flash::new_storage;
|
||||
use libtock_drivers::console::Console;
|
||||
use libtock_drivers::led;
|
||||
use ctap2::env::tock::take_storage;
|
||||
use libtock_console::Console;
|
||||
use libtock_drivers::result::FlexUnwrap;
|
||||
use libtock_leds::Leds;
|
||||
use libtock_platform as platform;
|
||||
use libtock_runtime::{set_main, stack_size, TockSyscalls};
|
||||
use persistent_store::{Storage, StorageIndex};
|
||||
use platform::DefaultConfig;
|
||||
|
||||
stack_size! {0x800}
|
||||
set_main! {main}
|
||||
|
||||
type Syscalls = TockSyscalls;
|
||||
|
||||
fn is_page_erased(storage: &dyn Storage, page: usize) -> bool {
|
||||
let index = StorageIndex { page, byte: 0 };
|
||||
@@ -34,20 +43,21 @@ fn is_page_erased(storage: &dyn Storage, page: usize) -> bool {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
led::get(1).flex_unwrap().on().flex_unwrap(); // red on dongle
|
||||
const NUM_PAGES: usize = 20; // should be at least ctap::storage::NUM_PAGES
|
||||
let mut storage = new_storage(NUM_PAGES);
|
||||
writeln!(Console::new(), "Erase {} pages of storage:", NUM_PAGES).unwrap();
|
||||
for page in 0..NUM_PAGES {
|
||||
write!(Console::new(), "- Page {} ", page).unwrap();
|
||||
Leds::<Syscalls>::on(1).map_err(|e| e.into()).flex_unwrap(); // red on dongle
|
||||
let mut storage = take_storage::<Syscalls, DefaultConfig>().unwrap();
|
||||
let num_pages = storage.num_pages();
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
writeln!(console, "Erase {} pages of storage:", num_pages).unwrap();
|
||||
for page in 0..num_pages {
|
||||
write!(console, "- Page {} ", page).unwrap();
|
||||
if is_page_erased(&storage, page) {
|
||||
writeln!(Console::new(), "skipped (was already erased).").unwrap();
|
||||
writeln!(console, "skipped (was already erased).").unwrap();
|
||||
} else {
|
||||
storage.erase_page(page).unwrap();
|
||||
writeln!(Console::new(), "erased.").unwrap();
|
||||
writeln!(console, "erased.").unwrap();
|
||||
}
|
||||
}
|
||||
writeln!(Console::new(), "Done.").unwrap();
|
||||
led::get(1).flex_unwrap().off().flex_unwrap();
|
||||
led::get(0).flex_unwrap().on().flex_unwrap(); // green on dongle
|
||||
writeln!(console, "Done.").unwrap();
|
||||
Leds::<Syscalls>::on(1).map_err(|e| e.into()).flex_unwrap();
|
||||
Leds::<Syscalls>::off(0).map_err(|e| e.into()).flex_unwrap(); // green on dongle
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
@@ -5,34 +6,35 @@ extern crate lang_items;
|
||||
extern crate libtock_drivers;
|
||||
|
||||
use core::fmt::Write;
|
||||
use libtock_drivers::console::Console;
|
||||
use libtock_console::{Console, ConsoleWriter};
|
||||
use libtock_runtime::{set_main, stack_size, TockSyscalls};
|
||||
|
||||
stack_size! {0x4000}
|
||||
set_main! {main}
|
||||
|
||||
type Syscalls = TockSyscalls;
|
||||
|
||||
#[cfg(not(feature = "with_nfc"))]
|
||||
mod example {
|
||||
use super::Console;
|
||||
use super::Write;
|
||||
use super::{ConsoleWriter, Syscalls, Write};
|
||||
|
||||
pub fn nfc(console: &mut Console) {
|
||||
pub fn nfc(console: &mut ConsoleWriter<Syscalls>) {
|
||||
writeln!(console, "NFC feature flag is missing!").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "with_nfc")]
|
||||
mod example {
|
||||
use super::Console;
|
||||
use super::Write;
|
||||
use libtock_core::result::CommandError;
|
||||
use libtock_drivers::nfc::NfcTag;
|
||||
use libtock_drivers::nfc::RecvOp;
|
||||
use libtock_drivers::result::FlexUnwrap;
|
||||
use libtock_drivers::result::TockError;
|
||||
use super::{Console, ConsoleWriter, Write};
|
||||
use crate::Syscalls;
|
||||
use libtock_drivers::nfc::{NfcTag, RecvOp};
|
||||
use libtock_drivers::result::{FlexUnwrap, TockError};
|
||||
use libtock_drivers::timer;
|
||||
use libtock_drivers::timer::Timer;
|
||||
use libtock_drivers::timer::Timestamp;
|
||||
use libtock_drivers::timer::{Timer, Timestamp};
|
||||
use libtock_platform::{DefaultConfig, ErrorCode};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
// The actual lint upper_case_acronyms is not supported in all toolchains.
|
||||
#[allow(clippy::all)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
enum ReturnCode {
|
||||
/// Operation completed successfully
|
||||
SUCCESS,
|
||||
@@ -52,16 +54,15 @@ mod example {
|
||||
ENOSUPPORT,
|
||||
}
|
||||
|
||||
impl From<isize> for ReturnCode {
|
||||
fn from(original: isize) -> ReturnCode {
|
||||
impl From<ErrorCode> for ReturnCode {
|
||||
fn from(original: ErrorCode) -> ReturnCode {
|
||||
match original {
|
||||
0 => ReturnCode::SUCCESS,
|
||||
-1 => ReturnCode::FAIL,
|
||||
-2 => ReturnCode::EBUSY,
|
||||
-4 => ReturnCode::EOFF,
|
||||
-6 => ReturnCode::EINVAL,
|
||||
-8 => ReturnCode::ECANCEL,
|
||||
-9 => ReturnCode::ENOMEM,
|
||||
ErrorCode::Fail => ReturnCode::FAIL,
|
||||
ErrorCode::Busy => ReturnCode::EBUSY,
|
||||
ErrorCode::Off => ReturnCode::EOFF,
|
||||
ErrorCode::Invalid => ReturnCode::EINVAL,
|
||||
ErrorCode::Cancel => ReturnCode::ECANCEL,
|
||||
ErrorCode::NoMem => ReturnCode::ENOMEM,
|
||||
_ => ReturnCode::ENOSUPPORT,
|
||||
}
|
||||
}
|
||||
@@ -70,34 +71,32 @@ mod example {
|
||||
/// Helper function to write on console the received packet.
|
||||
fn print_rx_buffer(buf: &mut [u8]) {
|
||||
if let Some((last, bytes)) = buf.split_last() {
|
||||
let mut console = Console::new();
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
write!(console, "RX:").unwrap();
|
||||
for byte in bytes {
|
||||
write!(console, " {:02x?}", byte).unwrap();
|
||||
}
|
||||
writeln!(console, " {:02x?}", last).unwrap();
|
||||
console.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/// Function to identify the time elapsed for a transmission request.
|
||||
fn bench_transmit(
|
||||
console: &mut Console,
|
||||
timer: &Timer,
|
||||
console: &mut ConsoleWriter<Syscalls>,
|
||||
timer: &Timer<Syscalls>,
|
||||
title: &str,
|
||||
mut buf: &mut [u8],
|
||||
buf: &mut [u8],
|
||||
) -> ReturnCode {
|
||||
let amount = buf.len();
|
||||
let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
|
||||
match NfcTag::transmit(&mut buf, amount) {
|
||||
let start =
|
||||
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
|
||||
match NfcTag::<Syscalls, DefaultConfig>::transmit(buf, amount as u32) {
|
||||
Ok(_) => (),
|
||||
Err(TockError::Command(CommandError {
|
||||
return_code: -8, /* ECANCEL: No Field*/
|
||||
..
|
||||
})) => return ReturnCode::ECANCEL,
|
||||
Err(_) => writeln!(Console::new(), " -- tx error!").unwrap(),
|
||||
Err(TockError::Command(ErrorCode::Cancel)) => return ReturnCode::ECANCEL,
|
||||
Err(_) => writeln!(console, " -- tx error!").unwrap(),
|
||||
}
|
||||
let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
|
||||
let end =
|
||||
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
|
||||
let elapsed = (end - start).ms();
|
||||
writeln!(
|
||||
console,
|
||||
@@ -108,21 +107,21 @@ mod example {
|
||||
(amount as f64) / elapsed * 8.
|
||||
)
|
||||
.unwrap();
|
||||
console.flush();
|
||||
|
||||
ReturnCode::SUCCESS
|
||||
}
|
||||
|
||||
fn receive_packet(console: &mut Console, mut buf: &mut [u8; 256]) -> ReturnCode {
|
||||
match NfcTag::receive(&mut buf) {
|
||||
fn receive_packet(console: &mut ConsoleWriter<Syscalls>, buf: &mut [u8; 256]) -> ReturnCode {
|
||||
match NfcTag::<Syscalls, DefaultConfig>::receive(buf) {
|
||||
Ok(RecvOp {
|
||||
recv_amount: amount,
|
||||
..
|
||||
}) => {
|
||||
if amount <= buf.len() {
|
||||
print_rx_buffer(&mut buf[..amount]);
|
||||
if amount <= buf.len() as u32 {
|
||||
print_rx_buffer(&mut buf[..amount as usize]);
|
||||
}
|
||||
}
|
||||
Err(TockError::Command(CommandError { return_code, .. })) => return return_code.into(),
|
||||
Err(TockError::Command(code)) => return code.into(),
|
||||
Err(_) => {
|
||||
writeln!(console, " -- RX Err").unwrap();
|
||||
return ReturnCode::ECANCEL;
|
||||
@@ -131,54 +130,58 @@ mod example {
|
||||
ReturnCode::SUCCESS
|
||||
}
|
||||
|
||||
fn transmit_reply(mut console: &mut Console, timer: &Timer, buf: &[u8]) -> ReturnCode {
|
||||
fn transmit_reply(
|
||||
console: &mut ConsoleWriter<Syscalls>,
|
||||
timer: &Timer<Syscalls>,
|
||||
buf: &[u8],
|
||||
) -> ReturnCode {
|
||||
let mut return_code = ReturnCode::SUCCESS;
|
||||
match buf[0] {
|
||||
0xe0 /* RATS */=> {
|
||||
let mut answer_to_select = [0x05, 0x78, 0x80, 0xB1, 0x00];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: ATS", &mut answer_to_select);
|
||||
return_code = bench_transmit(console, timer, "TX: ATS", &mut answer_to_select);
|
||||
}
|
||||
0xc2 /* DESELECT */ => {
|
||||
// Ignore the request
|
||||
let mut command_error = [0x6A, 0x81];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: DESELECT", &mut command_error);
|
||||
return_code = bench_transmit(console, timer, "TX: DESELECT", &mut command_error);
|
||||
}
|
||||
0x02 | 0x03 /* APDU Prefix */ => match buf[2] {
|
||||
// If the received packet is applet selection command (FIDO 2)
|
||||
0xa4 /* SELECT */ => if buf[3] == 0x04 && buf[5] == 0x08 && buf[6] == 0xa0 {
|
||||
// Vesion: "FIDO_2_0"
|
||||
let mut reply = [buf[0], 0x46, 0x49, 0x44, 0x4f, 0x5f, 0x32, 0x5f, 0x30, 0x90, 0x00,];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: Version Str", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: Version Str", &mut reply);
|
||||
} else if (buf[6] == 0xd2 && buf[7] == 0x76) || (buf[6] == 0xe1 && (buf[7] == 0x03 || buf[7] == 0x04)){
|
||||
let mut reply = [buf[0], 0x90, 0x00];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: 0x9000", &mut reply);
|
||||
} else /* Unknown file */ {
|
||||
let mut reply = [buf[0], 0x6a, 0x82];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: 0x6A82", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: 0x6A82", &mut reply);
|
||||
}
|
||||
0xb0 /* READ */ => match buf[5] {
|
||||
0x02 => {
|
||||
let mut reply = [buf[0], 0x12, 0x90, 0x00,];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: File Size", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: File Size", &mut reply);
|
||||
}
|
||||
0x12 => {
|
||||
let mut reply = [buf[0], 0xd1, 0x01, 0x0e, 0x55, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x70, 0x65,
|
||||
0x6e, 0x73, 0x6b, 0x2e, 0x64, 0x65, 0x76, 0x90, 0x00,];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: NDEF", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: NDEF", &mut reply);
|
||||
}
|
||||
0x0f => {
|
||||
let mut reply = [buf[0], 0x00, 0x0f, 0x20, 0x00, 0x7f, 0x00, 0x7f, 0x04, 0x06, 0xe1, 0x04,
|
||||
0x00, 0x7f, 0x00, 0x00, 0x90, 0x00,];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: CC", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: CC", &mut reply);
|
||||
}
|
||||
_ => {
|
||||
let mut reply = [buf[0], 0x90, 0x00];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: 0x9000", &mut reply);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let mut reply = [buf[0], 0x90, 0x00];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: 0x9000", &mut reply);
|
||||
}
|
||||
}
|
||||
0x26 | 0x52 | 0x50 /* REQA | WUPA | Halt */ => {
|
||||
@@ -189,31 +192,27 @@ mod example {
|
||||
return_code
|
||||
}
|
||||
|
||||
pub fn nfc(mut console: &mut Console) {
|
||||
pub fn nfc(console: &mut ConsoleWriter<Syscalls>) {
|
||||
// Setup the timer with a dummy callback (we only care about reading the current time, but the
|
||||
// API forces us to set an alarm callback too).
|
||||
let mut with_callback = timer::with_callback(|_, _| {});
|
||||
let mut with_callback = timer::with_callback(|_| {});
|
||||
|
||||
let timer = with_callback.init().flex_unwrap();
|
||||
|
||||
writeln!(
|
||||
console,
|
||||
"Clock frequency: {} Hz",
|
||||
timer.clock_frequency().hz()
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(console, "Clock frequency: {:?} Hz", timer.clock_frequency()).unwrap();
|
||||
|
||||
let mut state_change_counter = 0;
|
||||
loop {
|
||||
let mut rx_buf = [0; 256];
|
||||
match receive_packet(&mut console, &mut rx_buf) {
|
||||
match receive_packet(console, &mut rx_buf) {
|
||||
ReturnCode::EOFF => {
|
||||
// Not configured
|
||||
while !NfcTag::enable_emulation() {}
|
||||
while !NfcTag::<Syscalls, DefaultConfig>::enable_emulation() {}
|
||||
// Configure Type 4 tag
|
||||
while !NfcTag::configure(4) {}
|
||||
while !NfcTag::<Syscalls, DefaultConfig>::configure(4) {}
|
||||
}
|
||||
ReturnCode::ECANCEL /* field lost */ => {
|
||||
NfcTag::disable_emulation();
|
||||
NfcTag::<Syscalls, DefaultConfig>::disable_emulation();
|
||||
}
|
||||
ReturnCode::EBUSY /* awaiting select*/ => (),
|
||||
ReturnCode::ENOMEM => {
|
||||
@@ -224,9 +223,9 @@ mod example {
|
||||
ReturnCode::ENOSUPPORT => (),
|
||||
ReturnCode::SUCCESS => {
|
||||
// If the reader restarts the communication then disable the tag.
|
||||
match transmit_reply(&mut console, &timer, &rx_buf) {
|
||||
match transmit_reply(console, &timer, &rx_buf) {
|
||||
ReturnCode::ECANCEL | ReturnCode::EOFF => {
|
||||
if NfcTag::disable_emulation() {
|
||||
if NfcTag::<Syscalls, DefaultConfig>::disable_emulation() {
|
||||
writeln!(console, " -- TAG DISABLED").unwrap();
|
||||
}
|
||||
state_change_counter += 1;
|
||||
@@ -243,7 +242,7 @@ mod example {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut console = Console::new();
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
writeln!(console, "****************************************").unwrap();
|
||||
writeln!(console, "nfct_test application is installed").unwrap();
|
||||
example::nfc(&mut console);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
@@ -19,15 +20,22 @@ extern crate lang_items;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::Write;
|
||||
use libtock_drivers::console::Console;
|
||||
use libtock_console::Console;
|
||||
use libtock_runtime::{set_main, stack_size, TockSyscalls};
|
||||
|
||||
stack_size! {0x800}
|
||||
set_main! {main}
|
||||
|
||||
type Syscalls = TockSyscalls;
|
||||
|
||||
fn main() {
|
||||
writeln!(Console::new(), "****************************************").unwrap();
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
writeln!(console, "****************************************").unwrap();
|
||||
for i in 0.. {
|
||||
writeln!(Console::new(), "Allocating {} bytes...", 1 << i).unwrap();
|
||||
writeln!(console, "Allocating {} bytes...", 1 << i).unwrap();
|
||||
let x: Vec<u8> = Vec::with_capacity(1 << i);
|
||||
writeln!(Console::new(), "Allocated!").unwrap();
|
||||
writeln!(console, "Allocated!").unwrap();
|
||||
drop(x);
|
||||
writeln!(Console::new(), "Dropped!").unwrap();
|
||||
writeln!(console, "Dropped!").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate lang_items;
|
||||
|
||||
use libtock_runtime::{set_main, stack_size};
|
||||
|
||||
stack_size! {0x800}
|
||||
set_main! {main}
|
||||
|
||||
fn main() {
|
||||
panic!("Bye world!")
|
||||
}
|
||||
|
||||
@@ -12,51 +12,96 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
extern crate lang_items;
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
use core::fmt::Write;
|
||||
use ctap2::embedded_flash::{new_storage, Storage};
|
||||
use libtock_drivers::console::Console;
|
||||
use ctap2::env::tock::{take_storage, Storage};
|
||||
use libtock_console::Console;
|
||||
use libtock_drivers::result::FlexUnwrap;
|
||||
use libtock_drivers::timer::{self, Duration, Timer, Timestamp};
|
||||
use persistent_store::Store;
|
||||
use libtock_platform::DefaultConfig;
|
||||
use libtock_runtime::{set_main, stack_size, TockSyscalls};
|
||||
use persistent_store::{Storage as _, Store};
|
||||
|
||||
fn timestamp(timer: &Timer) -> Timestamp<f64> {
|
||||
Timestamp::<f64>::from_clock_value(timer.get_current_clock().ok().unwrap())
|
||||
stack_size! {0x800}
|
||||
set_main! {main}
|
||||
|
||||
type Syscalls = TockSyscalls;
|
||||
|
||||
fn timestamp(timer: &Timer<Syscalls>) -> Timestamp<f64> {
|
||||
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().ok().unwrap())
|
||||
}
|
||||
|
||||
fn measure<T>(timer: &Timer, operation: impl FnOnce() -> T) -> (T, Duration<f64>) {
|
||||
fn measure<T>(timer: &Timer<Syscalls>, operation: impl FnOnce() -> T) -> (T, Duration<f64>) {
|
||||
let before = timestamp(timer);
|
||||
let result = operation();
|
||||
let after = timestamp(timer);
|
||||
(result, after - before)
|
||||
}
|
||||
|
||||
// Only use one store at a time.
|
||||
unsafe fn boot_store(num_pages: usize, erase: bool) -> Store<Storage> {
|
||||
let mut storage = new_storage(num_pages);
|
||||
fn boot_store(
|
||||
mut storage: Storage<Syscalls, DefaultConfig>,
|
||||
erase: bool,
|
||||
) -> Store<Storage<Syscalls, DefaultConfig>> {
|
||||
let num_pages = storage.num_pages();
|
||||
if erase {
|
||||
for page in 0..num_pages {
|
||||
use persistent_store::Storage;
|
||||
storage.erase_page(page).unwrap();
|
||||
}
|
||||
}
|
||||
Store::new(storage).ok().unwrap()
|
||||
}
|
||||
|
||||
fn compute_latency(timer: &Timer, num_pages: usize, key_increment: usize, word_length: usize) {
|
||||
let mut console = Console::new();
|
||||
#[derive(Debug)]
|
||||
struct StorageConfig {
|
||||
num_pages: usize,
|
||||
}
|
||||
|
||||
fn storage_config(storage: &Storage<Syscalls, DefaultConfig>) -> StorageConfig {
|
||||
StorageConfig {
|
||||
num_pages: storage.num_pages(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Stat {
|
||||
key_increment: usize,
|
||||
entry_length: usize, // words
|
||||
boot_ms: f64,
|
||||
compaction_ms: f64,
|
||||
insert_ms: f64,
|
||||
remove_ms: f64,
|
||||
}
|
||||
|
||||
fn compute_latency(
|
||||
storage: Storage<Syscalls, DefaultConfig>,
|
||||
timer: &Timer<Syscalls>,
|
||||
num_pages: usize,
|
||||
key_increment: usize,
|
||||
word_length: usize,
|
||||
) -> (Storage<Syscalls, DefaultConfig>, Stat) {
|
||||
let mut stat = Stat {
|
||||
key_increment,
|
||||
entry_length: word_length,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
writeln!(
|
||||
console,
|
||||
"\nLatency for num_pages={} key_increment={} word_length={}.",
|
||||
num_pages, key_increment, word_length
|
||||
"\nLatency for key_increment={} word_length={}.",
|
||||
key_increment, word_length
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut store = unsafe { boot_store(num_pages, true) };
|
||||
let mut store = boot_store(storage, true);
|
||||
let total_capacity = store.capacity().unwrap().total();
|
||||
assert_eq!(store.capacity().unwrap().used(), 0);
|
||||
assert_eq!(store.lifetime().unwrap().used(), 0);
|
||||
@@ -72,33 +117,33 @@ fn compute_latency(timer: &Timer, num_pages: usize, key_increment: usize, word_l
|
||||
let ((), time) = measure(timer, || {
|
||||
for i in 0..count {
|
||||
let key = 1 + key_increment * i;
|
||||
// For some reason the kernel sometimes fails.
|
||||
while store.insert(key, &vec![0; 4 * word_length]).is_err() {
|
||||
// We never enter this loop in practice, but we still need it for the kernel.
|
||||
writeln!(console, "Retry insert.").unwrap();
|
||||
}
|
||||
store.insert(key, &vec![0; 4 * word_length]).unwrap();
|
||||
}
|
||||
});
|
||||
writeln!(console, "Setup: {:.1}ms for {} entries.", time.ms(), count).unwrap();
|
||||
|
||||
// Measure latency of insert.
|
||||
let key = 1 + key_increment * count;
|
||||
let ((), time) = measure(&timer, || {
|
||||
let ((), time) = measure(timer, || {
|
||||
store.insert(key, &vec![0; 4 * word_length]).unwrap()
|
||||
});
|
||||
writeln!(console, "Insert: {:.1}ms.", time.ms()).unwrap();
|
||||
stat.insert_ms = time.ms();
|
||||
assert_eq!(
|
||||
store.lifetime().unwrap().used(),
|
||||
num_pages + (1 + count) * (1 + word_length)
|
||||
);
|
||||
|
||||
// Measure latency of boot.
|
||||
let (mut store, time) = measure(&timer, || unsafe { boot_store(num_pages, false) });
|
||||
let storage = store.extract_storage();
|
||||
let (mut store, time) = measure(timer, || boot_store(storage, false));
|
||||
writeln!(console, "Boot: {:.1}ms.", time.ms()).unwrap();
|
||||
stat.boot_ms = time.ms();
|
||||
|
||||
// Measure latency of remove.
|
||||
let ((), time) = measure(&timer, || store.remove(key).unwrap());
|
||||
let ((), time) = measure(timer, || store.remove(key).unwrap());
|
||||
writeln!(console, "Remove: {:.1}ms.", time.ms()).unwrap();
|
||||
stat.remove_ms = time.ms();
|
||||
|
||||
// Measure latency of compaction.
|
||||
let length = total_capacity + num_pages - store.lifetime().unwrap().used();
|
||||
@@ -111,28 +156,84 @@ fn compute_latency(timer: &Timer, num_pages: usize, key_increment: usize, word_l
|
||||
assert_eq!(store.lifetime().unwrap().used(), num_pages + total_capacity);
|
||||
let ((), time) = measure(timer, || store.prepare(1).unwrap());
|
||||
writeln!(console, "Compaction: {:.1}ms.", time.ms()).unwrap();
|
||||
stat.compaction_ms = time.ms();
|
||||
assert!(store.lifetime().unwrap().used() > total_capacity + num_pages);
|
||||
|
||||
(store.extract_storage(), stat)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut with_callback = timer::with_callback(|_, _| {});
|
||||
let timer = with_callback.init().ok().unwrap();
|
||||
let mut with_callback = timer::with_callback::<Syscalls, DefaultConfig, _>(|_| {});
|
||||
|
||||
writeln!(Console::new(), "\nRunning 4 tests...").unwrap();
|
||||
// Those non-overwritten 50 words entries simulate credentials.
|
||||
compute_latency(&timer, 3, 1, 50);
|
||||
compute_latency(&timer, 20, 1, 50);
|
||||
// Those overwritten 1 word entries simulate counters.
|
||||
compute_latency(&timer, 3, 0, 1);
|
||||
compute_latency(&timer, 6, 0, 1);
|
||||
writeln!(Console::new(), "\nDone.").unwrap();
|
||||
let timer = with_callback.init().flex_unwrap();
|
||||
let storage = take_storage::<Syscalls, DefaultConfig>().unwrap();
|
||||
let config = storage_config(&storage);
|
||||
let mut stats = Vec::new();
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
|
||||
// Results on nrf52840dk:
|
||||
//
|
||||
// | Pages | Overwrite | Length | Boot | Compaction | Insert | Remove |
|
||||
// | ----- | --------- | --------- | ------- | ---------- | ------ | ------- |
|
||||
// | 3 | no | 50 words | 2.0 ms | 132.5 ms | 4.8 ms | 1.2 ms |
|
||||
// | 20 | no | 50 words | 7.4 ms | 135.5 ms | 10.2 ms | 3.9 ms |
|
||||
// | 3 | yes | 1 word | 21.9 ms | 94.5 ms | 12.4 ms | 5.9 ms |
|
||||
// | 6 | yes | 1 word | 55.2 ms | 100.8 ms | 24.8 ms | 12.1 ms |
|
||||
writeln!(console, "\nRunning 2 tests...").unwrap();
|
||||
// Simulate a store full of credentials (of 50 words).
|
||||
let (storage, stat) = compute_latency(storage, &timer, config.num_pages, 1, 50);
|
||||
stats.push(stat);
|
||||
// Simulate a store full of increments of a single counter.
|
||||
let (_storage, stat) = compute_latency(storage, &timer, config.num_pages, 0, 1);
|
||||
stats.push(stat);
|
||||
writeln!(console, "\nDone.\n").unwrap();
|
||||
|
||||
const HEADERS: &[&str] = &[
|
||||
"Overwrite",
|
||||
"Length",
|
||||
"Boot",
|
||||
"Compaction",
|
||||
"Insert",
|
||||
"Remove",
|
||||
];
|
||||
let mut matrix = vec![HEADERS.iter().map(|x| x.to_string()).collect()];
|
||||
for stat in stats {
|
||||
matrix.push(vec![
|
||||
if stat.key_increment == 0 { "yes" } else { "no" }.to_string(),
|
||||
format!("{} words", stat.entry_length),
|
||||
format!("{:.1} ms", stat.boot_ms),
|
||||
format!("{:.1} ms", stat.compaction_ms),
|
||||
format!("{:.1} ms", stat.insert_ms),
|
||||
format!("{:.1} ms", stat.remove_ms),
|
||||
]);
|
||||
}
|
||||
writeln!(console, "Copy to examples/store_latency.rs:\n").unwrap();
|
||||
writeln!(console, "{:?}", config).unwrap();
|
||||
write_matrix(matrix);
|
||||
|
||||
// Results for nrf52840dk_opensk:
|
||||
// StorageConfig { num_pages: 20 }
|
||||
// Overwrite Length Boot Compaction Insert Remove
|
||||
// no 50 words 18.6 ms 145.8 ms 21.0 ms 9.8 ms
|
||||
// yes 1 words 335.8 ms 100.6 ms 11.7 ms 5.7 ms
|
||||
}
|
||||
|
||||
fn align(x: &str, n: usize) {
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
for _ in 0..n.saturating_sub(x.len()) {
|
||||
write!(console, " ").unwrap();
|
||||
}
|
||||
write!(console, "{}", x).unwrap();
|
||||
}
|
||||
|
||||
fn write_matrix(mut m: Vec<Vec<String>>) {
|
||||
if m.is_empty() {
|
||||
return;
|
||||
}
|
||||
let num_cols = m.iter().map(|r| r.len()).max().unwrap();
|
||||
let mut col_len = vec![0; num_cols];
|
||||
for row in &mut m {
|
||||
row.resize(num_cols, String::new());
|
||||
for col in 0..num_cols {
|
||||
col_len[col] = core::cmp::max(col_len[col], row[col].len());
|
||||
}
|
||||
}
|
||||
for row in m {
|
||||
for col in 0..num_cols {
|
||||
align(&row[col], col_len[col] + 2 * (col > 0) as usize);
|
||||
}
|
||||
writeln!(Console::<Syscalls>::writer()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
589
fuzz/Cargo.lock
generated
589
fuzz/Cargo.lock
generated
@@ -1,589 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db55d72333851e17d572bec876e390cd3b11eb1ef53ae821dd9f3b653d2b4569"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cbor"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"byteorder",
|
||||
"cbor",
|
||||
"hex",
|
||||
"libtock_drivers",
|
||||
"rand",
|
||||
"regex",
|
||||
"ring",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"subtle",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctap2"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"byteorder",
|
||||
"cbor",
|
||||
"crypto",
|
||||
"lang_items",
|
||||
"libtock_core",
|
||||
"libtock_drivers",
|
||||
"persistent_store",
|
||||
"subtle",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctap2-fuzz"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"fuzz_helper",
|
||||
"libfuzzer-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
|
||||
[[package]]
|
||||
name = "fuzz_helper"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"cbor",
|
||||
"crypto",
|
||||
"ctap2",
|
||||
"lang_items",
|
||||
"libtock_drivers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lang_items"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libtock_core",
|
||||
"libtock_drivers",
|
||||
"linked_list_allocator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcf184a4b6b274f82a5df6b357da6055d3e82272327bba281c28bbba6f1664ef"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtock_codegen"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtock_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libtock_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtock_drivers"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libtock_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked_list_allocator"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "822add9edb1860698b79522510da17bef885171f75aa395cff099d770c609c24"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
|
||||
|
||||
[[package]]
|
||||
name = "persistent_store"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||
dependencies = [
|
||||
"autocfg 0.1.8",
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core 0.4.2",
|
||||
"rand_hc",
|
||||
"rand_isaac",
|
||||
"rand_jitter",
|
||||
"rand_os",
|
||||
"rand_pcg",
|
||||
"rand_xorshift",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||
dependencies = [
|
||||
"autocfg 0.1.8",
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
dependencies = [
|
||||
"rand_core 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_isaac"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_jitter"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_core 0.4.2",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_os"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||
dependencies = [
|
||||
"cloudabi",
|
||||
"fuchsia-cprng",
|
||||
"libc",
|
||||
"rand_core 0.4.2",
|
||||
"rdrand",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
||||
dependencies = [
|
||||
"autocfg 0.1.8",
|
||||
"rand_core 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rdrand"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
@@ -1,14 +0,0 @@
|
||||
[package]
|
||||
name = "fuzz_helper"
|
||||
version = "0.1.0"
|
||||
authors = ["Mingxiao Guo <mingxguo@google.com>"]
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
arrayref = "0.3.6"
|
||||
libtock_drivers = { path = "../../third_party/libtock-drivers" }
|
||||
crypto = { path = "../../libraries/crypto", features = ['std'] }
|
||||
cbor = { path = "../../libraries/cbor", features = ['std'] }
|
||||
ctap2 = { path = "../..", features = ['std'] }
|
||||
lang_items = { path = "../../third_party/lang-items", features = ['std'] }
|
||||
158
layout.ld
158
layout.ld
@@ -1,158 +0,0 @@
|
||||
/* Userland Generic Layout
|
||||
*
|
||||
* Currently, due to incomplete ROPI-RWPI support in rustc (see
|
||||
* https://github.com/tock/libtock-rs/issues/28), this layout implements static
|
||||
* linking. An application init script must define the FLASH and SRAM address
|
||||
* ranges as well as MPU_MIN_ALIGN before including this layout file.
|
||||
*
|
||||
* Here is a an example application linker script to get started:
|
||||
* MEMORY {
|
||||
* /* FLASH memory region must start immediately *after* the Tock
|
||||
* * Binary Format headers, which means you need to offset the
|
||||
* * beginning of FLASH memory region relative to where the
|
||||
* * application is loaded.
|
||||
* FLASH (rx) : ORIGIN = 0x10030, LENGTH = 0x0FFD0
|
||||
* SRAM (RWX) : ORIGIN = 0x20000, LENGTH = 0x10000
|
||||
* }
|
||||
* STACK_SIZE = 2048;
|
||||
* MPU_MIN_ALIGN = 8K;
|
||||
* INCLUDE ../libtock-rs/layout.ld
|
||||
*/
|
||||
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
/* Section for just the app crt0 header.
|
||||
* This must be first so that the app can find it.
|
||||
*/
|
||||
.crt0_header :
|
||||
{
|
||||
_beginning = .; /* Start of the app in flash. */
|
||||
/**
|
||||
* Populate the header expected by `crt0`:
|
||||
*
|
||||
* struct hdr {
|
||||
* uint32_t got_sym_start;
|
||||
* uint32_t got_start;
|
||||
* uint32_t got_size;
|
||||
* uint32_t data_sym_start;
|
||||
* uint32_t data_start;
|
||||
* uint32_t data_size;
|
||||
* uint32_t bss_start;
|
||||
* uint32_t bss_size;
|
||||
* uint32_t reldata_start;
|
||||
* uint32_t stack_size;
|
||||
* };
|
||||
*/
|
||||
/* Offset of GOT symbols in flash */
|
||||
LONG(LOADADDR(.got) - _beginning);
|
||||
/* Offset of GOT section in memory */
|
||||
LONG(_got);
|
||||
/* Size of GOT section */
|
||||
LONG(SIZEOF(.got));
|
||||
/* Offset of data symbols in flash */
|
||||
LONG(LOADADDR(.data) - _beginning);
|
||||
/* Offset of data section in memory */
|
||||
LONG(_data);
|
||||
/* Size of data section */
|
||||
LONG(SIZEOF(.data));
|
||||
/* Offset of BSS section in memory */
|
||||
LONG(_bss);
|
||||
/* Size of BSS section */
|
||||
LONG(SIZEOF(.bss));
|
||||
/* First address offset after program flash, where elf2tab places
|
||||
* .rel.data section */
|
||||
LONG(LOADADDR(.endflash) - _beginning);
|
||||
/* The size of the stack requested by this application */
|
||||
LONG(STACK_SIZE);
|
||||
/* Pad the header out to a multiple of 32 bytes so there is not a gap
|
||||
* between the header and subsequent .data section. It's unclear why,
|
||||
* but LLD is aligning sections to a multiple of 32 bytes. */
|
||||
. = ALIGN(32);
|
||||
} > FLASH =0xFF
|
||||
|
||||
/* Text section, Code! */
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_text = .;
|
||||
KEEP (*(.start))
|
||||
*(.text*)
|
||||
*(.rodata*)
|
||||
KEEP (*(.syscalls))
|
||||
*(.ARM.extab*)
|
||||
. = ALIGN(4); /* Make sure we're word-aligned here */
|
||||
_etext = .;
|
||||
} > FLASH =0xFF
|
||||
|
||||
/* Application stack */
|
||||
.stack :
|
||||
{
|
||||
. = . + STACK_SIZE;
|
||||
|
||||
_stack_top_unaligned = .;
|
||||
. = ALIGN(8);
|
||||
_stack_top_aligned = .;
|
||||
} > SRAM
|
||||
|
||||
/* Data section, static initialized variables
|
||||
* Note: This is placed in Flash after the text section, but needs to be
|
||||
* moved to SRAM at runtime
|
||||
*/
|
||||
.data : AT (_etext)
|
||||
{
|
||||
. = ALIGN(4); /* Make sure we're word-aligned here */
|
||||
_data = .;
|
||||
KEEP(*(.data*))
|
||||
. = ALIGN(4); /* Make sure we're word-aligned at the end of flash */
|
||||
} > SRAM
|
||||
|
||||
/* Global Offset Table */
|
||||
.got :
|
||||
{
|
||||
. = ALIGN(4); /* Make sure we're word-aligned here */
|
||||
_got = .;
|
||||
*(.got*)
|
||||
*(.got.plt*)
|
||||
. = ALIGN(4);
|
||||
} > SRAM
|
||||
|
||||
/* BSS section, static uninitialized variables */
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4); /* Make sure we're word-aligned here */
|
||||
_bss = .;
|
||||
KEEP(*(.bss*))
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
} > SRAM
|
||||
|
||||
/* End of flash. */
|
||||
.endflash :
|
||||
{
|
||||
} > FLASH
|
||||
|
||||
/* ARM Exception support
|
||||
*
|
||||
* This contains compiler-generated support for unwinding the stack,
|
||||
* consisting of key-value pairs of function addresses and information on
|
||||
* how to unwind stack frames.
|
||||
* https://wiki.linaro.org/KenWerner/Sandbox/libunwind?action=AttachFile&do=get&target=libunwind-LDS.pdf
|
||||
*
|
||||
* .ARM.exidx is sorted, so has to go in its own output section.
|
||||
*
|
||||
* __NOTE__: It's at the end because we currently don't actually serialize
|
||||
* it to the binary in elf2tbf. If it was before the RAM sections, it would
|
||||
* through off our calculations of the header.
|
||||
*/
|
||||
PROVIDE_HIDDEN (__exidx_start = .);
|
||||
.ARM.exidx :
|
||||
{
|
||||
/* (C++) Index entries for section unwinding */
|
||||
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||
} > FLASH
|
||||
PROVIDE_HIDDEN (__exidx_end = .);
|
||||
}
|
||||
|
||||
ASSERT((_stack_top_aligned - _stack_top_unaligned) == 0, "
|
||||
STACK_SIZE must be 8 byte multiple")
|
||||
5
libraries/cbor/Cargo.lock
generated
5
libraries/cbor/Cargo.lock
generated
@@ -1,5 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "cbor"
|
||||
version = "0.1.0"
|
||||
@@ -1,15 +1,22 @@
|
||||
[package]
|
||||
name = "cbor"
|
||||
version = "0.1.0"
|
||||
name = "sk-cbor"
|
||||
version = "0.1.2"
|
||||
authors = [
|
||||
"Fabian Kaczmarczyck <kaczmarczyck@google.com>",
|
||||
"Guillaume Endignoux <guillaumee@google.com>",
|
||||
"Jean-Michel Picod <jmichel@google.com>",
|
||||
"David Drysdale <drysdale@google.com>",
|
||||
]
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
description = "CBOR parsing library"
|
||||
homepage = "https://github.com/google/OpenSK"
|
||||
repository = "https://github.com/google/OpenSK"
|
||||
keywords = ["cbor", "serialization", "no_std"]
|
||||
categories = ["encoding"]
|
||||
readme = "README.md"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "passively-maintained" }
|
||||
|
||||
[dependencies]
|
||||
|
||||
[features]
|
||||
std = []
|
||||
|
||||
202
libraries/cbor/LICENSE
Normal file
202
libraries/cbor/LICENSE
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
54
libraries/cbor/README.md
Normal file
54
libraries/cbor/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# CBOR Parsing Library
|
||||
|
||||
[](https://crates.io/crates/sk-cbor)
|
||||
[](https://crates.io/crates/sk-cbor)
|
||||
[](https://docs.rs/sk-cbor)
|
||||
[](https://crates.io/crates/sk-cbor)
|
||||
[](https://crates.io/crates/sk-cbor)
|
||||
|
||||
This crate implements Concise Binary Object Representation (CBOR) from [RFC
|
||||
8949](https://datatracker.ietf.org/doc/html/rfc8949).
|
||||
|
||||
## Usage
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
// Build a CBOR object with the crate's convenience macros. Note that this
|
||||
// object is not built in canonical order.
|
||||
let map_object = cbor_map! {
|
||||
1 => cbor_array![2, 3],
|
||||
"tstr" => cbor_bytes!(vec![1, 2, 3]),
|
||||
-2 => cbor_null!(),
|
||||
3 => cbor_true!(),
|
||||
};
|
||||
|
||||
println!("Object {:?}", map_object);
|
||||
|
||||
// Serialize to bytes.
|
||||
let mut map_data = vec![];
|
||||
sk_cbor::writer::write(map_object, &mut map_data).unwrap();
|
||||
let hex_map_data = hex::encode(&map_data);
|
||||
|
||||
// Serialized version is in canonical order.
|
||||
println!("Serializes to {}", hex_map_data);
|
||||
assert_eq!(
|
||||
hex_map_data,
|
||||
concat!(
|
||||
"a4", // 4-map
|
||||
"01", // int(1) =>
|
||||
"820203", // 2-array [2, 3],
|
||||
"03", // int(3) =>
|
||||
"f5", // true,
|
||||
"21", // nint(-2) =>
|
||||
"f6", // null,
|
||||
"6474737472", // 4-tstr "tstr" =>
|
||||
"43010203" // 3-bstr
|
||||
)
|
||||
);
|
||||
|
||||
// Convert back to an object. This is different than the original object,
|
||||
// because the map is now in canonical order.
|
||||
let recovered_object = sk_cbor::reader::read(&map_data).unwrap();
|
||||
println!("Deserializes to {:?}", recovered_object);
|
||||
}
|
||||
```
|
||||
87
libraries/cbor/examples/cbor.rs
Normal file
87
libraries/cbor/examples/cbor.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Example program demonstrating cbor usage.
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use sk_cbor::values::Value;
|
||||
use sk_cbor::{cbor_array, cbor_bytes, cbor_map, cbor_null, cbor_true};
|
||||
|
||||
fn hexify(data: &[u8]) -> String {
|
||||
let mut s = String::new();
|
||||
for b in data {
|
||||
s.push_str(&format!("{:02x}", b));
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Build a CBOR object with various different types included. Note that this
|
||||
// object is not built in canonical order.
|
||||
let manual_object = Value::map(vec![
|
||||
(
|
||||
Value::from(1),
|
||||
Value::array(vec![Value::from(2), Value::from(3)]),
|
||||
),
|
||||
(Value::from("tstr".to_owned()), Value::from(vec![1, 2, 3])),
|
||||
(Value::from(-2), Value::null_value()),
|
||||
(Value::from(3), Value::bool_value(true)),
|
||||
]);
|
||||
|
||||
// Build the same object using the crate's convenience macros.
|
||||
let macro_object = cbor_map! {
|
||||
1 => cbor_array![2, 3],
|
||||
"tstr" => cbor_bytes!(vec![1, 2, 3]),
|
||||
-2 => cbor_null!(),
|
||||
3 => cbor_true!(),
|
||||
};
|
||||
|
||||
assert_eq!(manual_object, macro_object);
|
||||
println!("Object {:?}", manual_object);
|
||||
|
||||
// Serialize to bytes.
|
||||
let mut manual_data = vec![];
|
||||
sk_cbor::writer::write(manual_object, &mut manual_data).unwrap();
|
||||
let hex_manual_data = hexify(&manual_data);
|
||||
let mut macro_data = vec![];
|
||||
sk_cbor::writer::write(macro_object, &mut macro_data).unwrap();
|
||||
let hex_macro_data = hexify(¯o_data);
|
||||
|
||||
assert_eq!(hex_manual_data, hex_macro_data);
|
||||
|
||||
// Serialized version is in canonical order.
|
||||
println!("Serializes to {}", hex_manual_data);
|
||||
assert_eq!(
|
||||
hex_manual_data,
|
||||
concat!(
|
||||
"a4", // 4-map
|
||||
"01", // int(1) =>
|
||||
"820203", // 2-array [2, 3],
|
||||
"03", // int(3) =>
|
||||
"f5", // true,
|
||||
"21", // nint(-2) =>
|
||||
"f6", // null,
|
||||
"6474737472", // 4-tstr "tstr" =>
|
||||
"43010203" // 3-bstr
|
||||
)
|
||||
);
|
||||
|
||||
// Convert back to an object. This is different than the original object,
|
||||
// because the map is now in canonical order.
|
||||
let recovered_object = sk_cbor::reader::read(&manual_data).unwrap();
|
||||
println!("Deserializes to {:?}", recovered_object);
|
||||
}
|
||||
3
libraries/cbor/fuzz/.gitignore
vendored
Normal file
3
libraries/cbor/fuzz/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/artifacts/
|
||||
/corpus/
|
||||
/target/
|
||||
35
libraries/cbor/fuzz/Cargo.lock
generated
35
libraries/cbor/fuzz/Cargo.lock
generated
@@ -1,35 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db55d72333851e17d572bec876e390cd3b11eb1ef53ae821dd9f3b653d2b4569"
|
||||
|
||||
[[package]]
|
||||
name = "cbor"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "cbor-fuzz"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"cbor",
|
||||
"libfuzzer-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcf184a4b6b274f82a5df6b357da6055d3e82272327bba281c28bbba6f1664ef"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
[package]
|
||||
name = "cbor-fuzz"
|
||||
name = "sk-cbor-fuzz"
|
||||
version = "0.0.0"
|
||||
authors = ["Automatically generated"]
|
||||
publish = false
|
||||
@@ -12,7 +12,7 @@ cargo-fuzz = true
|
||||
[dependencies]
|
||||
libfuzzer-sys = "0.3"
|
||||
|
||||
[dependencies.cbor]
|
||||
[dependencies.sk-cbor]
|
||||
path = ".."
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
|
||||
@@ -3,11 +3,12 @@ extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use sk_cbor as cbor;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
if let Ok(value) = cbor::read(data) {
|
||||
let mut result = Vec::new();
|
||||
assert!(cbor::write(value, &mut result));
|
||||
assert!(cbor::write(value, &mut result).is_ok());
|
||||
assert_eq!(result, data);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -12,11 +12,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
#[cfg(feature = "std")]
|
||||
extern crate core;
|
||||
|
||||
pub mod macros;
|
||||
pub mod reader;
|
||||
@@ -24,5 +22,5 @@ pub mod values;
|
||||
pub mod writer;
|
||||
|
||||
pub use self::reader::read;
|
||||
pub use self::values::{KeyType, SimpleValue, Value};
|
||||
pub use self::values::Value;
|
||||
pub use self::writer::write;
|
||||
|
||||
@@ -12,15 +12,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::values::{KeyType, Value};
|
||||
use alloc::collections::btree_map;
|
||||
//! Convenience macros for working with CBOR values.
|
||||
|
||||
use crate::values::Value;
|
||||
use alloc::vec;
|
||||
use core::cmp::Ordering;
|
||||
use core::iter::Peekable;
|
||||
|
||||
/// This macro generates code to extract multiple values from a `BTreeMap<KeyType, Value>` at once
|
||||
/// in an optimized manner, consuming the input map.
|
||||
/// This macro generates code to extract multiple values from a `Vec<(Value, Value)>` at once
|
||||
/// in an optimized manner, consuming the input vector.
|
||||
///
|
||||
/// It takes as input a `BTreeMap` as well as a list of identifiers and keys, and generates code
|
||||
/// It takes as input a `Vec` as well as a list of identifiers and keys, and generates code
|
||||
/// that assigns the corresponding values to new variables using the given identifiers. Each of
|
||||
/// these variables has type `Option<Value>`, to account for the case where keys aren't found.
|
||||
///
|
||||
@@ -32,16 +34,14 @@ use core::iter::Peekable;
|
||||
/// the keys are indeed sorted. This macro is therefore **not suitable for dynamic keys** that can
|
||||
/// change at runtime.
|
||||
///
|
||||
/// Semantically, provided that the keys are sorted as specified above, the following two snippets
|
||||
/// of code are equivalent, but the `destructure_cbor_map!` version is more optimized, as it doesn't
|
||||
/// re-balance the `BTreeMap` for each key, contrary to the `BTreeMap::remove` operations.
|
||||
/// Example usage:
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate alloc;
|
||||
/// # use cbor::destructure_cbor_map;
|
||||
/// # use sk_cbor::destructure_cbor_map;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # let map = alloc::collections::BTreeMap::new();
|
||||
/// # let map = alloc::vec::Vec::new();
|
||||
/// destructure_cbor_map! {
|
||||
/// let {
|
||||
/// 1 => x,
|
||||
@@ -50,17 +50,6 @@ use core::iter::Peekable;
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate alloc;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # let mut map = alloc::collections::BTreeMap::<cbor::KeyType, _>::new();
|
||||
/// use cbor::values::IntoCborKey;
|
||||
/// let x: Option<cbor::Value> = map.remove(&1.into_cbor_key());
|
||||
/// let y: Option<cbor::Value> = map.remove(&"key".into_cbor_key());
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! destructure_cbor_map {
|
||||
( let { $( $key:expr => $variable:ident, )+ } = $map:expr; ) => {
|
||||
@@ -70,7 +59,7 @@ macro_rules! destructure_cbor_map {
|
||||
#[cfg(test)]
|
||||
$crate::assert_sorted_keys!($( $key, )+);
|
||||
|
||||
use $crate::values::{IntoCborKey, Value};
|
||||
use $crate::values::{IntoCborValue, Value};
|
||||
use $crate::macros::destructure_cbor_map_peek_value;
|
||||
|
||||
// This algorithm first converts the map into a peekable iterator - whose items are sorted
|
||||
@@ -83,7 +72,7 @@ macro_rules! destructure_cbor_map {
|
||||
// to come in the same order (i.e. sorted).
|
||||
let mut it = $map.into_iter().peekable();
|
||||
$(
|
||||
let $variable: Option<Value> = destructure_cbor_map_peek_value(&mut it, $key.into_cbor_key());
|
||||
let $variable: Option<Value> = destructure_cbor_map_peek_value(&mut it, $key.into_cbor_value());
|
||||
)+
|
||||
};
|
||||
}
|
||||
@@ -100,14 +89,14 @@ macro_rules! destructure_cbor_map {
|
||||
/// would be inlined for every use case. As of June 2020, this saves ~40KB of binary size for the
|
||||
/// CTAP2 application of OpenSK.
|
||||
pub fn destructure_cbor_map_peek_value(
|
||||
it: &mut Peekable<btree_map::IntoIter<KeyType, Value>>,
|
||||
needle: KeyType,
|
||||
it: &mut Peekable<vec::IntoIter<(Value, Value)>>,
|
||||
needle: Value,
|
||||
) -> Option<Value> {
|
||||
loop {
|
||||
match it.peek() {
|
||||
None => return None,
|
||||
Some(item) => {
|
||||
let key: &KeyType = &item.0;
|
||||
let key: &Value = &item.0;
|
||||
match key.cmp(&needle) {
|
||||
Ordering::Less => {
|
||||
it.next();
|
||||
@@ -123,6 +112,7 @@ pub fn destructure_cbor_map_peek_value(
|
||||
}
|
||||
}
|
||||
|
||||
/// Assert that the keys in a vector of key-value pairs are in canonical order.
|
||||
#[macro_export]
|
||||
macro_rules! assert_sorted_keys {
|
||||
// Last key
|
||||
@@ -131,9 +121,9 @@ macro_rules! assert_sorted_keys {
|
||||
|
||||
( $key1:expr, $key2:expr, $( $keys:expr, )* ) => {
|
||||
{
|
||||
use $crate::values::{IntoCborKey, KeyType};
|
||||
let k1: KeyType = $key1.into_cbor_key();
|
||||
let k2: KeyType = $key2.into_cbor_key();
|
||||
use $crate::values::{IntoCborValue, Value};
|
||||
let k1: Value = $key1.into_cbor_value();
|
||||
let k2: Value = $key2.into_cbor_value();
|
||||
assert!(
|
||||
k1 < k2,
|
||||
"{:?} < {:?} failed. The destructure_cbor_map! macro requires keys in sorted order.",
|
||||
@@ -145,6 +135,26 @@ macro_rules! assert_sorted_keys {
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Map with the specified key-value pairs.
|
||||
///
|
||||
/// Keys and values are expressions and converted into CBOR Keys and Values.
|
||||
/// The syntax for these pairs is `key_expression => value_expression,`.
|
||||
/// Keys do not have to be sorted.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// You may not call this function with identical keys in its argument.
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate alloc;
|
||||
/// # use sk_cbor::cbor_map;
|
||||
/// let map = cbor_map! {
|
||||
/// 0x01 => false,
|
||||
/// "02" => -3,
|
||||
/// };
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! cbor_map {
|
||||
// trailing comma case
|
||||
@@ -156,16 +166,36 @@ macro_rules! cbor_map {
|
||||
{
|
||||
// The import is unused if the list is empty.
|
||||
#[allow(unused_imports)]
|
||||
use $crate::values::{IntoCborKey, IntoCborValue};
|
||||
let mut _map = ::alloc::collections::BTreeMap::new();
|
||||
use $crate::values::IntoCborValue;
|
||||
let mut _map = ::alloc::vec::Vec::new();
|
||||
$(
|
||||
_map.insert($key.into_cbor_key(), $value.into_cbor_value());
|
||||
_map.push(($key.into_cbor_value(), $value.into_cbor_value()));
|
||||
)*
|
||||
$crate::values::Value::Map(_map)
|
||||
$crate::values::Value::map(_map)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Map with key-value pairs where values can be Options.
|
||||
///
|
||||
/// Keys and values are expressions and converted into CBOR Keys and Value Options.
|
||||
/// The map entry is included iff the Value is not an Option or Option is Some.
|
||||
/// The syntax for these pairs is `key_expression => value_expression,`.
|
||||
/// Duplicate keys will lead to invalid CBOR, i.e. writing these values fails.
|
||||
/// Keys do not have to be sorted.
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate alloc;
|
||||
/// # use sk_cbor::cbor_map_options;
|
||||
/// let missing_value: Option<bool> = None;
|
||||
/// let map = cbor_map_options! {
|
||||
/// 0x01 => Some(false),
|
||||
/// "02" => -3,
|
||||
/// "not in map" => missing_value,
|
||||
/// };
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! cbor_map_options {
|
||||
// trailing comma case
|
||||
@@ -177,28 +207,40 @@ macro_rules! cbor_map_options {
|
||||
{
|
||||
// The import is unused if the list is empty.
|
||||
#[allow(unused_imports)]
|
||||
use $crate::values::{IntoCborKey, IntoCborValueOption};
|
||||
let mut _map = ::alloc::collections::BTreeMap::<_, $crate::values::Value>::new();
|
||||
use $crate::values::{IntoCborValue, IntoCborValueOption};
|
||||
let mut _map = ::alloc::vec::Vec::<(_, $crate::values::Value)>::new();
|
||||
$(
|
||||
{
|
||||
let opt: Option<$crate::values::Value> = $value.into_cbor_value_option();
|
||||
if let Some(val) = opt {
|
||||
_map.insert($key.into_cbor_key(), val);
|
||||
_map.push(($key.into_cbor_value(), val));
|
||||
}
|
||||
}
|
||||
)*
|
||||
$crate::values::Value::Map(_map)
|
||||
$crate::values::Value::map(_map)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Map from a Vec<(Value, Value)>.
|
||||
#[macro_export]
|
||||
macro_rules! cbor_map_btree {
|
||||
( $tree:expr ) => {
|
||||
$crate::values::Value::Map($tree)
|
||||
};
|
||||
macro_rules! cbor_map_collection {
|
||||
( $tree:expr ) => {{
|
||||
$crate::values::Value::map($tree)
|
||||
}};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Array with the given elements.
|
||||
///
|
||||
/// Elements are expressions and converted into CBOR Values. Elements are comma-separated.
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate alloc;
|
||||
/// # use sk_cbor::cbor_array;
|
||||
/// let array = cbor_array![1, "2"];
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! cbor_array {
|
||||
// trailing comma case
|
||||
@@ -211,47 +253,53 @@ macro_rules! cbor_array {
|
||||
// The import is unused if the list is empty.
|
||||
#[allow(unused_imports)]
|
||||
use $crate::values::IntoCborValue;
|
||||
$crate::values::Value::Array(vec![ $( $value.into_cbor_value(), )* ])
|
||||
$crate::values::Value::array(vec![ $( $value.into_cbor_value(), )* ])
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Array from a Vec<Value>.
|
||||
#[macro_export]
|
||||
macro_rules! cbor_array_vec {
|
||||
( $vec:expr ) => {{
|
||||
use $crate::values::IntoCborValue;
|
||||
$crate::values::Value::Array($vec.into_iter().map(|x| x.into_cbor_value()).collect())
|
||||
$crate::values::Value::array($vec.into_iter().map(|x| x.into_cbor_value()).collect())
|
||||
}};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Simple with value true.
|
||||
#[macro_export]
|
||||
macro_rules! cbor_true {
|
||||
( ) => {
|
||||
$crate::values::Value::Simple($crate::values::SimpleValue::TrueValue)
|
||||
$crate::values::Value::bool_value(true)
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Simple with value false.
|
||||
#[macro_export]
|
||||
macro_rules! cbor_false {
|
||||
( ) => {
|
||||
$crate::values::Value::Simple($crate::values::SimpleValue::FalseValue)
|
||||
$crate::values::Value::bool_value(false)
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Simple with value null.
|
||||
#[macro_export]
|
||||
macro_rules! cbor_null {
|
||||
( ) => {
|
||||
$crate::values::Value::Simple($crate::values::SimpleValue::NullValue)
|
||||
$crate::values::Value::null_value()
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Simple with the undefined value.
|
||||
#[macro_export]
|
||||
macro_rules! cbor_undefined {
|
||||
( ) => {
|
||||
$crate::values::Value::Simple($crate::values::SimpleValue::Undefined)
|
||||
$crate::values::Value::undefined()
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Simple with the given bool value.
|
||||
#[macro_export]
|
||||
macro_rules! cbor_bool {
|
||||
( $x:expr ) => {
|
||||
@@ -259,37 +307,55 @@ macro_rules! cbor_bool {
|
||||
};
|
||||
}
|
||||
|
||||
// For key types, we construct a KeyType and call .into(), which will automatically convert it to a
|
||||
// KeyType or a Value depending on the context.
|
||||
/// Creates a CBOR Value of type Unsigned with the given numeric value.
|
||||
#[macro_export]
|
||||
macro_rules! cbor_unsigned {
|
||||
( $x:expr ) => {
|
||||
$crate::cbor_key_unsigned!($x).into()
|
||||
$crate::values::Value::unsigned($x as u64)
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Unsigned or Negative with the given numeric value.
|
||||
#[macro_export]
|
||||
macro_rules! cbor_int {
|
||||
( $x:expr ) => {
|
||||
$crate::cbor_key_int!($x).into()
|
||||
$crate::values::Value::integer($x as i64)
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Text String with the given string.
|
||||
#[macro_export]
|
||||
macro_rules! cbor_text {
|
||||
( $x:expr ) => {
|
||||
$crate::cbor_key_text!($x).into()
|
||||
$crate::values::Value::text_string($x.into())
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Byte String with the given slice or vector.
|
||||
#[macro_export]
|
||||
macro_rules! cbor_bytes {
|
||||
( $x:expr ) => {
|
||||
$crate::cbor_key_bytes!($x).into()
|
||||
$crate::values::Value::byte_string($x)
|
||||
};
|
||||
}
|
||||
|
||||
// Macro to use with a literal, e.g. cbor_bytes_lit!(b"foo")
|
||||
/// Creates a CBOR Value of type Tag with the given tag and object.
|
||||
#[macro_export]
|
||||
macro_rules! cbor_tagged {
|
||||
( $t:expr, $x: expr ) => {
|
||||
$crate::values::Value::tag($t, $x)
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a CBOR Value of type Byte String with the given byte string literal.
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate alloc;
|
||||
/// # use sk_cbor::cbor_bytes_lit;
|
||||
/// let byte_array = cbor_bytes_lit!(b"foo");
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! cbor_bytes_lit {
|
||||
( $x:expr ) => {
|
||||
@@ -297,100 +363,70 @@ macro_rules! cbor_bytes_lit {
|
||||
};
|
||||
}
|
||||
|
||||
// Some explicit macros are also available for contexts where the type is not explicit.
|
||||
#[macro_export]
|
||||
macro_rules! cbor_key_unsigned {
|
||||
( $x:expr ) => {
|
||||
$crate::values::KeyType::Unsigned($x)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cbor_key_int {
|
||||
( $x:expr ) => {
|
||||
$crate::values::KeyType::integer($x)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cbor_key_text {
|
||||
( $x:expr ) => {
|
||||
$crate::values::KeyType::TextString($x.into())
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cbor_key_bytes {
|
||||
( $x:expr ) => {
|
||||
$crate::values::KeyType::ByteString($x)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::super::values::{KeyType, SimpleValue, Value};
|
||||
use alloc::collections::BTreeMap;
|
||||
use super::super::values::Value;
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
#[test]
|
||||
fn test_cbor_simple_values() {
|
||||
assert_eq!(cbor_true!(), Value::Simple(SimpleValue::TrueValue));
|
||||
assert_eq!(cbor_false!(), Value::Simple(SimpleValue::FalseValue));
|
||||
assert_eq!(cbor_null!(), Value::Simple(SimpleValue::NullValue));
|
||||
assert_eq!(cbor_undefined!(), Value::Simple(SimpleValue::Undefined));
|
||||
assert_eq!(cbor_true!(), Value::bool_value(true));
|
||||
assert_eq!(cbor_false!(), Value::bool_value(false));
|
||||
assert_eq!(cbor_null!(), Value::null_value());
|
||||
assert_eq!(cbor_undefined!(), Value::undefined());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cbor_bool() {
|
||||
assert_eq!(cbor_bool!(true), Value::Simple(SimpleValue::TrueValue));
|
||||
assert_eq!(cbor_bool!(false), Value::Simple(SimpleValue::FalseValue));
|
||||
assert_eq!(cbor_bool!(true), Value::bool_value(true));
|
||||
assert_eq!(cbor_bool!(false), Value::bool_value(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cbor_int_unsigned() {
|
||||
assert_eq!(cbor_key_int!(0), KeyType::Unsigned(0));
|
||||
assert_eq!(cbor_key_int!(1), KeyType::Unsigned(1));
|
||||
assert_eq!(cbor_key_int!(123456), KeyType::Unsigned(123456));
|
||||
assert_eq!(cbor_int!(0), Value::from(0));
|
||||
assert_eq!(cbor_int!(1), Value::from(1));
|
||||
assert_eq!(cbor_int!(123456), Value::from(123456));
|
||||
assert_eq!(
|
||||
cbor_key_int!(std::i64::MAX),
|
||||
KeyType::Unsigned(std::i64::MAX as u64)
|
||||
cbor_int!(core::i64::MAX),
|
||||
Value::from(core::i64::MAX as u64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cbor_int_negative() {
|
||||
assert_eq!(cbor_key_int!(-1), KeyType::Negative(-1));
|
||||
assert_eq!(cbor_key_int!(-123456), KeyType::Negative(-123456));
|
||||
assert_eq!(
|
||||
cbor_key_int!(std::i64::MIN),
|
||||
KeyType::Negative(std::i64::MIN)
|
||||
);
|
||||
assert_eq!(cbor_int!(-1), Value::from(-1));
|
||||
assert_eq!(cbor_int!(-123456), Value::from(-123456));
|
||||
assert_eq!(cbor_int!(core::i64::MIN), Value::from(core::i64::MIN));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cbor_int_literals() {
|
||||
let a = cbor_array![
|
||||
std::i64::MIN,
|
||||
std::i32::MIN,
|
||||
core::i64::MIN,
|
||||
core::i32::MIN,
|
||||
-123456,
|
||||
-1,
|
||||
0,
|
||||
1,
|
||||
123456,
|
||||
std::i32::MAX,
|
||||
std::i64::MAX,
|
||||
std::u64::MAX,
|
||||
core::i32::MAX,
|
||||
core::i64::MAX,
|
||||
core::u64::MAX,
|
||||
];
|
||||
let b = Value::Array(vec![
|
||||
Value::KeyValue(KeyType::Negative(std::i64::MIN)),
|
||||
Value::KeyValue(KeyType::Negative(std::i32::MIN as i64)),
|
||||
Value::KeyValue(KeyType::Negative(-123456)),
|
||||
Value::KeyValue(KeyType::Negative(-1)),
|
||||
Value::KeyValue(KeyType::Unsigned(0)),
|
||||
Value::KeyValue(KeyType::Unsigned(1)),
|
||||
Value::KeyValue(KeyType::Unsigned(123456)),
|
||||
Value::KeyValue(KeyType::Unsigned(std::i32::MAX as u64)),
|
||||
Value::KeyValue(KeyType::Unsigned(std::i64::MAX as u64)),
|
||||
Value::KeyValue(KeyType::Unsigned(std::u64::MAX)),
|
||||
let b = Value::array(vec![
|
||||
Value::from(core::i64::MIN),
|
||||
Value::from(core::i32::MIN as i64),
|
||||
Value::from(-123456),
|
||||
Value::from(-1),
|
||||
Value::from(0),
|
||||
Value::from(1),
|
||||
Value::from(123456),
|
||||
Value::from(core::i32::MAX as u64),
|
||||
Value::from(core::i64::MAX as u64),
|
||||
Value::from(core::u64::MAX),
|
||||
]);
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
@@ -409,25 +445,17 @@ mod test {
|
||||
cbor_map! {},
|
||||
cbor_map! {2 => 3},
|
||||
];
|
||||
let b = Value::Array(vec![
|
||||
Value::KeyValue(KeyType::Negative(-123)),
|
||||
Value::KeyValue(KeyType::Unsigned(456)),
|
||||
Value::Simple(SimpleValue::TrueValue),
|
||||
Value::Simple(SimpleValue::NullValue),
|
||||
Value::KeyValue(KeyType::TextString(String::from("foo"))),
|
||||
Value::KeyValue(KeyType::ByteString(b"bar".to_vec())),
|
||||
Value::Array(Vec::new()),
|
||||
Value::Array(vec![
|
||||
Value::KeyValue(KeyType::Unsigned(0)),
|
||||
Value::KeyValue(KeyType::Unsigned(1)),
|
||||
]),
|
||||
Value::Map(BTreeMap::new()),
|
||||
Value::Map(
|
||||
[(KeyType::Unsigned(2), Value::KeyValue(KeyType::Unsigned(3)))]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
),
|
||||
let b = Value::array(vec![
|
||||
Value::from(-123),
|
||||
Value::from(456),
|
||||
Value::bool_value(true),
|
||||
Value::null_value(),
|
||||
Value::from(String::from("foo")),
|
||||
Value::from(b"bar".to_vec()),
|
||||
Value::array(Vec::new()),
|
||||
Value::array(vec![Value::from(0), Value::from(1)]),
|
||||
Value::map(Vec::new()),
|
||||
Value::map([(Value::from(2), Value::from(3))].iter().cloned().collect()),
|
||||
]);
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
@@ -435,18 +463,18 @@ mod test {
|
||||
#[test]
|
||||
fn test_cbor_array_vec_empty() {
|
||||
let a = cbor_array_vec!(Vec::<bool>::new());
|
||||
let b = Value::Array(Vec::new());
|
||||
let b = Value::array(Vec::new());
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cbor_array_vec_int() {
|
||||
let a = cbor_array_vec!(vec![1, 2, 3, 4]);
|
||||
let b = Value::Array(vec![
|
||||
Value::KeyValue(KeyType::Unsigned(1)),
|
||||
Value::KeyValue(KeyType::Unsigned(2)),
|
||||
Value::KeyValue(KeyType::Unsigned(3)),
|
||||
Value::KeyValue(KeyType::Unsigned(4)),
|
||||
let b = Value::array(vec![
|
||||
Value::from(1),
|
||||
Value::from(2),
|
||||
Value::from(3),
|
||||
Value::from(4),
|
||||
]);
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
@@ -454,10 +482,10 @@ mod test {
|
||||
#[test]
|
||||
fn test_cbor_array_vec_text() {
|
||||
let a = cbor_array_vec!(vec!["a", "b", "c"]);
|
||||
let b = Value::Array(vec![
|
||||
Value::KeyValue(KeyType::TextString(String::from("a"))),
|
||||
Value::KeyValue(KeyType::TextString(String::from("b"))),
|
||||
Value::KeyValue(KeyType::TextString(String::from("c"))),
|
||||
let b = Value::array(vec![
|
||||
Value::from(String::from("a")),
|
||||
Value::from(String::from("b")),
|
||||
Value::from(String::from("c")),
|
||||
]);
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
@@ -465,10 +493,10 @@ mod test {
|
||||
#[test]
|
||||
fn test_cbor_array_vec_bytes() {
|
||||
let a = cbor_array_vec!(vec![b"a", b"b", b"c"]);
|
||||
let b = Value::Array(vec![
|
||||
Value::KeyValue(KeyType::ByteString(b"a".to_vec())),
|
||||
Value::KeyValue(KeyType::ByteString(b"b".to_vec())),
|
||||
Value::KeyValue(KeyType::ByteString(b"c".to_vec())),
|
||||
let b = Value::array(vec![
|
||||
Value::from(b"a".to_vec()),
|
||||
Value::from(b"b".to_vec()),
|
||||
Value::from(b"c".to_vec()),
|
||||
]);
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
@@ -476,58 +504,35 @@ mod test {
|
||||
#[test]
|
||||
fn test_cbor_map() {
|
||||
let a = cbor_map! {
|
||||
-1 => -23,
|
||||
4 => 56,
|
||||
"foo" => true,
|
||||
b"bar" => cbor_null!(),
|
||||
5 => "foo",
|
||||
6 => b"bar",
|
||||
7 => cbor_array![],
|
||||
8 => cbor_array![0, 1],
|
||||
9 => cbor_map!{},
|
||||
10 => cbor_map!{2 => 3},
|
||||
-1 => -23,
|
||||
b"bar" => cbor_null!(),
|
||||
"foo" => true,
|
||||
};
|
||||
let b = Value::Map(
|
||||
let b = Value::map(
|
||||
[
|
||||
(Value::from(4), Value::from(56)),
|
||||
(Value::from(5), Value::from(String::from("foo"))),
|
||||
(Value::from(6), Value::from(b"bar".to_vec())),
|
||||
(Value::from(7), Value::array(Vec::new())),
|
||||
(
|
||||
KeyType::Negative(-1),
|
||||
Value::KeyValue(KeyType::Negative(-23)),
|
||||
Value::from(8),
|
||||
Value::array(vec![Value::from(0), Value::from(1)]),
|
||||
),
|
||||
(KeyType::Unsigned(4), Value::KeyValue(KeyType::Unsigned(56))),
|
||||
(Value::from(9), Value::map(Vec::new())),
|
||||
(
|
||||
KeyType::TextString(String::from("foo")),
|
||||
Value::Simple(SimpleValue::TrueValue),
|
||||
),
|
||||
(
|
||||
KeyType::ByteString(b"bar".to_vec()),
|
||||
Value::Simple(SimpleValue::NullValue),
|
||||
),
|
||||
(
|
||||
KeyType::Unsigned(5),
|
||||
Value::KeyValue(KeyType::TextString(String::from("foo"))),
|
||||
),
|
||||
(
|
||||
KeyType::Unsigned(6),
|
||||
Value::KeyValue(KeyType::ByteString(b"bar".to_vec())),
|
||||
),
|
||||
(KeyType::Unsigned(7), Value::Array(Vec::new())),
|
||||
(
|
||||
KeyType::Unsigned(8),
|
||||
Value::Array(vec![
|
||||
Value::KeyValue(KeyType::Unsigned(0)),
|
||||
Value::KeyValue(KeyType::Unsigned(1)),
|
||||
]),
|
||||
),
|
||||
(KeyType::Unsigned(9), Value::Map(BTreeMap::new())),
|
||||
(
|
||||
KeyType::Unsigned(10),
|
||||
Value::Map(
|
||||
[(KeyType::Unsigned(2), Value::KeyValue(KeyType::Unsigned(3)))]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
),
|
||||
Value::from(10),
|
||||
Value::map([(Value::from(2), Value::from(3))].iter().cloned().collect()),
|
||||
),
|
||||
(Value::from(-1), Value::from(-23)),
|
||||
(Value::from(b"bar".to_vec()), Value::null_value()),
|
||||
(Value::from(String::from("foo")), Value::bool_value(true)),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
@@ -539,66 +544,43 @@ mod test {
|
||||
#[test]
|
||||
fn test_cbor_map_options() {
|
||||
let a = cbor_map_options! {
|
||||
-1 => -23,
|
||||
4 => Some(56),
|
||||
11 => None::<String>,
|
||||
"foo" => true,
|
||||
12 => None::<&str>,
|
||||
b"bar" => Some(cbor_null!()),
|
||||
13 => None::<Vec<u8>>,
|
||||
5 => "foo",
|
||||
14 => None::<&[u8]>,
|
||||
6 => Some(b"bar" as &[u8]),
|
||||
15 => None::<bool>,
|
||||
7 => cbor_array![],
|
||||
16 => None::<i32>,
|
||||
8 => Some(cbor_array![0, 1]),
|
||||
17 => None::<i64>,
|
||||
9 => cbor_map!{},
|
||||
18 => None::<u64>,
|
||||
10 => Some(cbor_map!{2 => 3}),
|
||||
11 => None::<String>,
|
||||
12 => None::<&str>,
|
||||
13 => None::<Vec<u8>>,
|
||||
14 => None::<&[u8]>,
|
||||
15 => None::<bool>,
|
||||
16 => None::<i32>,
|
||||
17 => None::<i64>,
|
||||
18 => None::<u64>,
|
||||
-1 => -23,
|
||||
b"bar" => Some(cbor_null!()),
|
||||
"foo" => true,
|
||||
};
|
||||
let b = Value::Map(
|
||||
let b = Value::map(
|
||||
[
|
||||
(Value::from(4), Value::from(56)),
|
||||
(Value::from(5), Value::from(String::from("foo"))),
|
||||
(Value::from(6), Value::from(b"bar".to_vec())),
|
||||
(Value::from(7), Value::array(Vec::new())),
|
||||
(
|
||||
KeyType::Negative(-1),
|
||||
Value::KeyValue(KeyType::Negative(-23)),
|
||||
Value::from(8),
|
||||
Value::array(vec![Value::from(0), Value::from(1)]),
|
||||
),
|
||||
(KeyType::Unsigned(4), Value::KeyValue(KeyType::Unsigned(56))),
|
||||
(Value::from(9), Value::map(Vec::new())),
|
||||
(
|
||||
KeyType::TextString(String::from("foo")),
|
||||
Value::Simple(SimpleValue::TrueValue),
|
||||
),
|
||||
(
|
||||
KeyType::ByteString(b"bar".to_vec()),
|
||||
Value::Simple(SimpleValue::NullValue),
|
||||
),
|
||||
(
|
||||
KeyType::Unsigned(5),
|
||||
Value::KeyValue(KeyType::TextString(String::from("foo"))),
|
||||
),
|
||||
(
|
||||
KeyType::Unsigned(6),
|
||||
Value::KeyValue(KeyType::ByteString(b"bar".to_vec())),
|
||||
),
|
||||
(KeyType::Unsigned(7), Value::Array(Vec::new())),
|
||||
(
|
||||
KeyType::Unsigned(8),
|
||||
Value::Array(vec![
|
||||
Value::KeyValue(KeyType::Unsigned(0)),
|
||||
Value::KeyValue(KeyType::Unsigned(1)),
|
||||
]),
|
||||
),
|
||||
(KeyType::Unsigned(9), Value::Map(BTreeMap::new())),
|
||||
(
|
||||
KeyType::Unsigned(10),
|
||||
Value::Map(
|
||||
[(KeyType::Unsigned(2), Value::KeyValue(KeyType::Unsigned(3)))]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
),
|
||||
Value::from(10),
|
||||
Value::map([(Value::from(2), Value::from(3))].iter().cloned().collect()),
|
||||
),
|
||||
(Value::from(-1), Value::from(-23)),
|
||||
(Value::from(b"bar".to_vec()), Value::null_value()),
|
||||
(Value::from(String::from("foo")), Value::bool_value(true)),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
@@ -608,34 +590,21 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cbor_map_btree_empty() {
|
||||
let a = cbor_map_btree!(BTreeMap::new());
|
||||
let b = Value::Map(BTreeMap::new());
|
||||
fn test_cbor_map_collection_empty() {
|
||||
let a = cbor_map_collection!(Vec::<(_, _)>::new());
|
||||
let b = Value::map(Vec::new());
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cbor_map_btree_foo() {
|
||||
let a = cbor_map_btree!(
|
||||
[(KeyType::Unsigned(2), Value::KeyValue(KeyType::Unsigned(3)))]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect()
|
||||
);
|
||||
let b = Value::Map(
|
||||
[(KeyType::Unsigned(2), Value::KeyValue(KeyType::Unsigned(3)))]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
);
|
||||
fn test_cbor_map_collection_foo() {
|
||||
let a = cbor_map_collection!(vec![(Value::from(2), Value::from(3))]);
|
||||
let b = Value::map(vec![(Value::from(2), Value::from(3))]);
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
|
||||
fn extract_map(cbor_value: Value) -> BTreeMap<KeyType, Value> {
|
||||
match cbor_value {
|
||||
Value::Map(map) => map,
|
||||
_ => panic!("Expected CBOR map."),
|
||||
}
|
||||
fn extract_map(cbor_value: Value) -> Vec<(Value, Value)> {
|
||||
cbor_value.extract_map().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -725,4 +694,22 @@ mod test {
|
||||
assert_eq!(x4, Some(cbor_unsigned!(40)));
|
||||
assert_eq!(x5, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_destructure_unsorted_cbor_map() {
|
||||
let map = cbor_map! {
|
||||
2 => 20,
|
||||
1 => 10,
|
||||
};
|
||||
|
||||
destructure_cbor_map! {
|
||||
let {
|
||||
1 => x1,
|
||||
2 => x2,
|
||||
} = extract_map(map);
|
||||
}
|
||||
|
||||
assert_eq!(x1, Some(cbor_unsigned!(10)));
|
||||
assert_eq!(x2, Some(cbor_unsigned!(20)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,21 +12,24 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::values::{Constants, KeyType, SimpleValue, Value};
|
||||
use crate::{cbor_array_vec, cbor_bytes_lit, cbor_map_btree, cbor_text, cbor_unsigned};
|
||||
use alloc::collections::BTreeMap;
|
||||
//! Functionality for deserializing CBOR data into values.
|
||||
|
||||
use super::values::{Constants, SimpleValue, Value, ValueImpl};
|
||||
use crate::{
|
||||
cbor_array_vec, cbor_bytes_lit, cbor_map_collection, cbor_tagged, cbor_text, cbor_unsigned,
|
||||
};
|
||||
use alloc::str;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Possible errors from a deserialization operation.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum DecoderError {
|
||||
UnsupportedMajorType,
|
||||
UnknownAdditionalInfo,
|
||||
IncompleteCborData,
|
||||
IncorrectMapKeyType,
|
||||
TooMuchNesting,
|
||||
InvalidUtf8,
|
||||
ExtranousData,
|
||||
ExtraneousData,
|
||||
OutOfOrderKey,
|
||||
NonMinimalCborEncoding,
|
||||
UnsupportedSimpleValue,
|
||||
@@ -34,11 +37,21 @@ pub enum DecoderError {
|
||||
OutOfRangeIntegerValue,
|
||||
}
|
||||
|
||||
/// Deserialize CBOR binary data to produce a single [`Value`], expecting that there is no additional data.
|
||||
/// Maximum level of nesting supported is 127; more deeply nested structures will fail with
|
||||
/// [`DecoderError::TooMuchNesting`].
|
||||
pub fn read(encoded_cbor: &[u8]) -> Result<Value, DecoderError> {
|
||||
read_nested(encoded_cbor, Some(i8::MAX))
|
||||
}
|
||||
|
||||
/// Deserialize CBOR binary data to produce a single [`Value`], expecting that there is no additional data. If
|
||||
/// `max_nest` is `Some(max)`, then nested structures are only supported up to the given limit (returning
|
||||
/// [`DecoderError::TooMuchNesting`] if the limit is hit).
|
||||
pub fn read_nested(encoded_cbor: &[u8], max_nest: Option<i8>) -> Result<Value, DecoderError> {
|
||||
let mut reader = Reader::new(encoded_cbor);
|
||||
let value = reader.decode_complete_data_item(Reader::MAX_NESTING_DEPTH)?;
|
||||
let value = reader.decode_complete_data_item(max_nest)?;
|
||||
if !reader.remaining_cbor.is_empty() {
|
||||
return Err(DecoderError::ExtranousData);
|
||||
return Err(DecoderError::ExtraneousData);
|
||||
}
|
||||
Ok(value)
|
||||
}
|
||||
@@ -48,8 +61,6 @@ struct Reader<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Reader<'a> {
|
||||
const MAX_NESTING_DEPTH: i8 = 4;
|
||||
|
||||
pub fn new(cbor: &'a [u8]) -> Reader<'a> {
|
||||
Reader {
|
||||
remaining_cbor: cbor,
|
||||
@@ -58,9 +69,9 @@ impl<'a> Reader<'a> {
|
||||
|
||||
pub fn decode_complete_data_item(
|
||||
&mut self,
|
||||
remaining_depth: i8,
|
||||
remaining_depth: Option<i8>,
|
||||
) -> Result<Value, DecoderError> {
|
||||
if remaining_depth < 0 {
|
||||
if remaining_depth.map_or(false, |d| d < 0) {
|
||||
return Err(DecoderError::TooMuchNesting);
|
||||
}
|
||||
|
||||
@@ -69,19 +80,17 @@ impl<'a> Reader<'a> {
|
||||
// Unsigned byte means logical shift, so only zeros get shifted in.
|
||||
let major_type_value = first_byte >> Constants::MAJOR_TYPE_BIT_SHIFT;
|
||||
let additional_info = first_byte & Constants::ADDITIONAL_INFORMATION_MASK;
|
||||
let size_result = self.read_variadic_length_integer(additional_info);
|
||||
match size_result {
|
||||
Ok(size_value) => match major_type_value {
|
||||
0 => self.decode_value_to_unsigned(size_value),
|
||||
1 => self.decode_value_to_negative(size_value),
|
||||
2 => self.read_byte_string_content(size_value),
|
||||
3 => self.read_text_string_content(size_value),
|
||||
4 => self.read_array_content(size_value, remaining_depth),
|
||||
5 => self.read_map_content(size_value, remaining_depth),
|
||||
7 => self.decode_to_simple_value(size_value, additional_info),
|
||||
_ => Err(DecoderError::UnsupportedMajorType),
|
||||
},
|
||||
Err(decode_error) => Err(decode_error),
|
||||
let size_value = self.read_variadic_length_integer(additional_info)?;
|
||||
match major_type_value {
|
||||
0 => self.decode_value_to_unsigned(size_value),
|
||||
1 => self.decode_value_to_negative(size_value),
|
||||
2 => self.read_byte_string_content(size_value),
|
||||
3 => self.read_text_string_content(size_value),
|
||||
4 => self.read_array_content(size_value, remaining_depth),
|
||||
5 => self.read_map_content(size_value, remaining_depth),
|
||||
6 => self.read_tagged_content(size_value, remaining_depth),
|
||||
7 => self.decode_to_simple_value(size_value, additional_info),
|
||||
_ => Err(DecoderError::UnsupportedMajorType),
|
||||
}
|
||||
}
|
||||
_ => Err(DecoderError::IncompleteCborData),
|
||||
@@ -135,7 +144,7 @@ impl<'a> Reader<'a> {
|
||||
if signed_size < 0 {
|
||||
Err(DecoderError::OutOfRangeIntegerValue)
|
||||
} else {
|
||||
Ok(Value::KeyValue(KeyType::Negative(-(size_value as i64) - 1)))
|
||||
Ok(Value(ValueImpl::Negative(-(size_value as i64) - 1)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,12 +168,12 @@ impl<'a> Reader<'a> {
|
||||
fn read_array_content(
|
||||
&mut self,
|
||||
size_value: u64,
|
||||
remaining_depth: i8,
|
||||
remaining_depth: Option<i8>,
|
||||
) -> Result<Value, DecoderError> {
|
||||
// Don't set the capacity already, it is an unsanitized input.
|
||||
let mut value_array = Vec::new();
|
||||
for _ in 0..size_value {
|
||||
value_array.push(self.decode_complete_data_item(remaining_depth - 1)?);
|
||||
value_array.push(self.decode_complete_data_item(remaining_depth.map(|d| d - 1))?);
|
||||
}
|
||||
Ok(cbor_array_vec!(value_array))
|
||||
}
|
||||
@@ -172,25 +181,31 @@ impl<'a> Reader<'a> {
|
||||
fn read_map_content(
|
||||
&mut self,
|
||||
size_value: u64,
|
||||
remaining_depth: i8,
|
||||
remaining_depth: Option<i8>,
|
||||
) -> Result<Value, DecoderError> {
|
||||
let mut value_map = BTreeMap::new();
|
||||
let mut last_key_option = None;
|
||||
let mut value_map = Vec::<(Value, Value)>::new();
|
||||
for _ in 0..size_value {
|
||||
let key_value = self.decode_complete_data_item(remaining_depth - 1)?;
|
||||
if let Value::KeyValue(key) = key_value {
|
||||
if let Some(last_key) = last_key_option {
|
||||
if last_key >= key {
|
||||
return Err(DecoderError::OutOfOrderKey);
|
||||
}
|
||||
let key = self.decode_complete_data_item(remaining_depth.map(|d| d - 1))?;
|
||||
if let Some(last_item) = value_map.last() {
|
||||
if last_item.0 >= key {
|
||||
return Err(DecoderError::OutOfOrderKey);
|
||||
}
|
||||
last_key_option = Some(key.clone());
|
||||
value_map.insert(key, self.decode_complete_data_item(remaining_depth - 1)?);
|
||||
} else {
|
||||
return Err(DecoderError::IncorrectMapKeyType);
|
||||
}
|
||||
value_map.push((
|
||||
key,
|
||||
self.decode_complete_data_item(remaining_depth.map(|d| d - 1))?,
|
||||
));
|
||||
}
|
||||
Ok(cbor_map_btree!(value_map))
|
||||
Ok(cbor_map_collection!(value_map))
|
||||
}
|
||||
|
||||
fn read_tagged_content(
|
||||
&mut self,
|
||||
tag_value: u64,
|
||||
remaining_depth: Option<i8>,
|
||||
) -> Result<Value, DecoderError> {
|
||||
let inner_value = self.decode_complete_data_item(remaining_depth.map(|d| d - 1))?;
|
||||
Ok(cbor_tagged!(tag_value, inner_value))
|
||||
}
|
||||
|
||||
fn decode_to_simple_value(
|
||||
@@ -206,7 +221,7 @@ impl<'a> Reader<'a> {
|
||||
return Err(DecoderError::UnsupportedFloatingPointValue);
|
||||
}
|
||||
match SimpleValue::from_integer(size_value) {
|
||||
Some(simple_value) => Ok(Value::Simple(simple_value)),
|
||||
Some(simple_value) => Ok(Value(ValueImpl::Simple(simple_value))),
|
||||
None => Err(DecoderError::UnsupportedSimpleValue),
|
||||
}
|
||||
}
|
||||
@@ -219,6 +234,7 @@ mod test {
|
||||
cbor_array, cbor_bytes, cbor_false, cbor_int, cbor_map, cbor_null, cbor_true,
|
||||
cbor_undefined,
|
||||
};
|
||||
use alloc::vec;
|
||||
|
||||
#[test]
|
||||
fn test_read_unsigned() {
|
||||
@@ -238,14 +254,14 @@ mod test {
|
||||
vec![0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00],
|
||||
),
|
||||
(
|
||||
std::i64::MAX,
|
||||
core::i64::MAX,
|
||||
vec![0x1B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
|
||||
),
|
||||
];
|
||||
for (unsigned, mut cbor) in cases {
|
||||
assert_eq!(read(&cbor), Ok(cbor_int!(unsigned)));
|
||||
cbor.push(0x01);
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,14 +316,14 @@ mod test {
|
||||
(-1000000, vec![0x3A, 0x00, 0x0F, 0x42, 0x3F]),
|
||||
(-4294967296, vec![0x3A, 0xFF, 0xFF, 0xFF, 0xFF]),
|
||||
(
|
||||
std::i64::MIN,
|
||||
core::i64::MIN,
|
||||
vec![0x3B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
|
||||
),
|
||||
];
|
||||
for (negative, mut cbor) in cases {
|
||||
assert_eq!(read(&cbor), Ok(cbor_int!(negative)));
|
||||
cbor.push(0x01);
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +339,7 @@ mod test {
|
||||
for (byte_string, mut cbor) in cases {
|
||||
assert_eq!(read(&cbor), Ok(cbor_bytes!(byte_string)));
|
||||
cbor.push(0x01);
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,7 +353,7 @@ mod test {
|
||||
("\"\\", vec![0x62, 0x22, 0x5C]),
|
||||
("ü", vec![0x62, 0xC3, 0xBC]),
|
||||
(
|
||||
std::str::from_utf8(&unicode_3byte).unwrap(),
|
||||
core::str::from_utf8(&unicode_3byte).unwrap(),
|
||||
vec![0x63, 0xE6, 0xB0, 0xB4],
|
||||
),
|
||||
("𐅑", vec![0x64, 0xF0, 0x90, 0x85, 0x91]),
|
||||
@@ -345,7 +361,7 @@ mod test {
|
||||
for (text_string, mut cbor) in cases {
|
||||
assert_eq!(read(&cbor), Ok(cbor_text!(text_string)));
|
||||
cbor.push(0x01);
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,7 +399,7 @@ mod test {
|
||||
for (text_string, mut cbor) in cases {
|
||||
assert_eq!(read(&cbor), Ok(cbor_text!(text_string)));
|
||||
cbor.push(0x01);
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,7 +421,7 @@ mod test {
|
||||
];
|
||||
assert_eq!(read(&test_cbor.clone()), Ok(cbor_array_vec!(value_vec)));
|
||||
test_cbor.push(0x01);
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -429,7 +445,7 @@ mod test {
|
||||
];
|
||||
assert_eq!(read(&test_cbor), Ok(value_map));
|
||||
test_cbor.push(0x01);
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -453,7 +469,7 @@ mod test {
|
||||
];
|
||||
assert_eq!(read(&test_cbor), Ok(value_map));
|
||||
test_cbor.push(0x01);
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -474,7 +490,7 @@ mod test {
|
||||
];
|
||||
assert_eq!(read(&test_cbor), Ok(value_map));
|
||||
test_cbor.push(0x01);
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -492,7 +508,7 @@ mod test {
|
||||
];
|
||||
assert_eq!(read(&test_cbor), Ok(value_map));
|
||||
test_cbor.push(0x01);
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -509,7 +525,7 @@ mod test {
|
||||
];
|
||||
assert_eq!(read(&test_cbor), Ok(value_map));
|
||||
test_cbor.push(0x01);
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -526,7 +542,7 @@ mod test {
|
||||
];
|
||||
assert_eq!(read(&test_cbor), Ok(value_map));
|
||||
test_cbor.push(0x01);
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -548,7 +564,36 @@ mod test {
|
||||
];
|
||||
assert_eq!(read(&test_cbor), Ok(value_map));
|
||||
test_cbor.push(0x01);
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&test_cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_tagged() {
|
||||
let cases = vec![
|
||||
(cbor_tagged!(6, cbor_int!(0x42)), vec![0xc6, 0x18, 0x42]),
|
||||
(cbor_tagged!(1, cbor_true!()), vec![0xc1, 0xf5]),
|
||||
(
|
||||
cbor_tagged!(
|
||||
1000,
|
||||
cbor_map! {
|
||||
"a" => 1,
|
||||
"b" => cbor_array![2, 3],
|
||||
}
|
||||
),
|
||||
vec![
|
||||
0xd9, 0x03, 0xe8, 0xa2, // map of 2 pairs
|
||||
0x61, 0x61, // "a"
|
||||
0x01, 0x61, 0x62, // "b"
|
||||
0x82, // array with 2 elements
|
||||
0x02, 0x03,
|
||||
],
|
||||
),
|
||||
];
|
||||
for (value, mut cbor) in cases {
|
||||
assert_eq!(read(&cbor), Ok(value));
|
||||
cbor.push(0x01);
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -574,7 +619,7 @@ mod test {
|
||||
for (simple, mut cbor) in cases {
|
||||
assert_eq!(read(&cbor.clone()), Ok(simple));
|
||||
cbor.push(0x01);
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,19 +660,6 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_unsupported_map_key_format_error() {
|
||||
// While CBOR can handle all types as map keys, we only support a subset.
|
||||
let bad_map_cbor = vec![
|
||||
0xa2, // map of 2 pairs
|
||||
0x82, 0x01, 0x02, // invalid key : [1, 2]
|
||||
0x02, // value : 2
|
||||
0x61, 0x64, // key : "d"
|
||||
0x03, // value : 3
|
||||
];
|
||||
assert_eq!(read(&bad_map_cbor), Err(DecoderError::IncorrectMapKeyType));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_unknown_additional_info_error() {
|
||||
let cases = vec![
|
||||
@@ -656,7 +688,7 @@ mod test {
|
||||
];
|
||||
for cbor in cases {
|
||||
let mut reader = Reader::new(&cbor);
|
||||
assert!(reader.decode_complete_data_item(0).is_ok());
|
||||
assert!(reader.decode_complete_data_item(Some(0)).is_ok());
|
||||
}
|
||||
let map_cbor = vec![
|
||||
0xa2, // map of 2 pairs
|
||||
@@ -667,11 +699,11 @@ mod test {
|
||||
];
|
||||
let mut reader = Reader::new(&map_cbor);
|
||||
assert_eq!(
|
||||
reader.decode_complete_data_item(1),
|
||||
reader.decode_complete_data_item(Some(1)),
|
||||
Err(DecoderError::TooMuchNesting)
|
||||
);
|
||||
reader = Reader::new(&map_cbor);
|
||||
assert!(reader.decode_complete_data_item(2).is_ok());
|
||||
assert!(reader.decode_complete_data_item(Some(2)).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -756,7 +788,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_extranous_cbor_data_error() {
|
||||
fn test_read_extraneous_cbor_data_error() {
|
||||
let cases = vec![
|
||||
vec![0x19, 0x03, 0x05, 0x00],
|
||||
vec![0x44, 0x01, 0x02, 0x03, 0x04, 0x00],
|
||||
@@ -765,7 +797,7 @@ mod test {
|
||||
vec![0xa1, 0x61, 0x63, 0x02, 0x61, 0x64, 0x03],
|
||||
];
|
||||
for cbor in cases {
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtranousData));
|
||||
assert_eq!(read(&cbor), Err(DecoderError::ExtraneousData));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -797,20 +829,4 @@ mod test {
|
||||
assert_eq!(read(&cbor), Err(DecoderError::IncompleteCborData));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_unsupported_major_type() {
|
||||
let cases = vec![
|
||||
vec![0xC0],
|
||||
vec![0xD8, 0xFF],
|
||||
// multi-dimensional array example using tags
|
||||
vec![
|
||||
0x82, 0x82, 0x02, 0x03, 0xd8, 0x41, 0x4a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00,
|
||||
0x03, 0x00, 0x04, 0x00, 0x05,
|
||||
],
|
||||
];
|
||||
for cbor in cases {
|
||||
assert_eq!(read(&cbor), Err(DecoderError::UnsupportedMajorType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,93 +12,242 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use alloc::collections::BTreeMap;
|
||||
//! Types for expressing CBOR values.
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp::Ordering;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Value {
|
||||
KeyValue(KeyType),
|
||||
/// The CBOR data structure.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Value(pub(crate) ValueImpl);
|
||||
|
||||
/// Possible CBOR values.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum ValueImpl {
|
||||
/// Unsigned integer value (uint).
|
||||
Unsigned(u64),
|
||||
/// Signed integer value (nint). Only 63 bits of information are used here.
|
||||
Negative(i64),
|
||||
/// Byte string (bstr).
|
||||
ByteString(Vec<u8>),
|
||||
/// Text string (tstr).
|
||||
TextString(String),
|
||||
/// Array/tuple of values.
|
||||
Array(Vec<Value>),
|
||||
Map(BTreeMap<KeyType, Value>),
|
||||
// TAG is omitted
|
||||
/// Map of key-value pairs.
|
||||
Map(Vec<(Value, Value)>),
|
||||
/// Tagged value.
|
||||
Tag(u64, Box<Value>),
|
||||
/// Simple value.
|
||||
Simple(SimpleValue),
|
||||
}
|
||||
|
||||
// The specification recommends to limit the available keys.
|
||||
// Currently supported are both integer and string types.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum KeyType {
|
||||
Unsigned(u64),
|
||||
// We only use 63 bits of information here.
|
||||
Negative(i64),
|
||||
ByteString(Vec<u8>),
|
||||
TextString(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum SimpleValue {
|
||||
/// Specific simple CBOR values.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum SimpleValue {
|
||||
FalseValue = 20,
|
||||
TrueValue = 21,
|
||||
NullValue = 22,
|
||||
Undefined = 23,
|
||||
}
|
||||
|
||||
/// Constant values required for CBOR encoding.
|
||||
pub struct Constants {}
|
||||
|
||||
impl Constants {
|
||||
/// Number of bits used to shift left the major type of a CBOR type byte.
|
||||
pub const MAJOR_TYPE_BIT_SHIFT: u8 = 5;
|
||||
/// Mask to retrieve the additional information held in a CBOR type bytes,
|
||||
/// ignoring the major type.
|
||||
pub const ADDITIONAL_INFORMATION_MASK: u8 = 0x1F;
|
||||
/// Additional information value that indicates the largest inline value.
|
||||
pub const ADDITIONAL_INFORMATION_MAX_INT: u8 = 23;
|
||||
/// Additional information value indicating that a 1-byte length follows.
|
||||
pub const ADDITIONAL_INFORMATION_1_BYTE: u8 = 24;
|
||||
/// Additional information value indicating that a 2-byte length follows.
|
||||
pub const ADDITIONAL_INFORMATION_2_BYTES: u8 = 25;
|
||||
/// Additional information value indicating that a 4-byte length follows.
|
||||
pub const ADDITIONAL_INFORMATION_4_BYTES: u8 = 26;
|
||||
/// Additional information value indicating that an 8-byte length follows.
|
||||
pub const ADDITIONAL_INFORMATION_8_BYTES: u8 = 27;
|
||||
}
|
||||
|
||||
impl Value {
|
||||
/// Creates a CBOR unsigned value.
|
||||
pub fn unsigned(int: u64) -> Value {
|
||||
Value(ValueImpl::Unsigned(int))
|
||||
}
|
||||
|
||||
/// Create an appropriate CBOR integer value (uint/nint).
|
||||
/// For simplicity, this only takes i64. Construct directly for the last bit.
|
||||
pub fn integer(int: i64) -> Value {
|
||||
if int >= 0 {
|
||||
Value(ValueImpl::Unsigned(int as u64))
|
||||
} else {
|
||||
Value(ValueImpl::Negative(int))
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a CBOR byte string value.
|
||||
pub fn byte_string(bytes: Vec<u8>) -> Value {
|
||||
Value(ValueImpl::ByteString(bytes))
|
||||
}
|
||||
|
||||
/// Creates a CBOR text string value.
|
||||
pub fn text_string(text: String) -> Value {
|
||||
Value(ValueImpl::TextString(text))
|
||||
}
|
||||
|
||||
/// Create a CBOR array value.
|
||||
pub fn array(a: Vec<Value>) -> Value {
|
||||
Value(ValueImpl::Array(a))
|
||||
}
|
||||
|
||||
/// Create a CBOR map value.
|
||||
///
|
||||
/// Keys do not have to be sorted.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// You may not call this function with identical keys in its argument.
|
||||
pub fn map(mut m: Vec<(Value, Value)>) -> Value {
|
||||
m.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
let map_len = m.len();
|
||||
m.dedup_by(|a, b| a.0.eq(&b.0));
|
||||
if map_len != m.len() {
|
||||
panic!();
|
||||
}
|
||||
Value(ValueImpl::Map(m))
|
||||
}
|
||||
|
||||
/// Create a CBOR tagged value.
|
||||
pub fn tag(int: u64, value: Value) -> Value {
|
||||
Value(ValueImpl::Tag(int, Box::new(value)))
|
||||
}
|
||||
|
||||
/// Create a CBOR boolean simple value.
|
||||
pub fn bool_value(b: bool) -> Value {
|
||||
if b {
|
||||
Value::Simple(SimpleValue::TrueValue)
|
||||
Value(ValueImpl::Simple(SimpleValue::TrueValue))
|
||||
} else {
|
||||
Value::Simple(SimpleValue::FalseValue)
|
||||
Value(ValueImpl::Simple(SimpleValue::FalseValue))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_label(&self) -> u8 {
|
||||
/// Creates a null value.
|
||||
pub fn null_value() -> Value {
|
||||
Value(ValueImpl::Simple(SimpleValue::NullValue))
|
||||
}
|
||||
|
||||
/// Creates an undefined value.
|
||||
pub fn undefined() -> Value {
|
||||
Value(ValueImpl::Simple(SimpleValue::Undefined))
|
||||
}
|
||||
|
||||
pub fn extract_unsigned(self) -> Option<u64> {
|
||||
match self {
|
||||
Value::KeyValue(key) => key.type_label(),
|
||||
Value::Array(_) => 4,
|
||||
Value::Map(_) => 5,
|
||||
Value::Simple(_) => 7,
|
||||
Value(ValueImpl::Unsigned(unsigned)) => Some(unsigned),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_integer(self) -> Option<i64> {
|
||||
match self {
|
||||
Value(ValueImpl::Unsigned(unsigned)) => {
|
||||
if unsigned <= core::i64::MAX as u64 {
|
||||
Some(unsigned as i64)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Value(ValueImpl::Negative(signed)) => Some(signed),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_byte_string(self) -> Option<Vec<u8>> {
|
||||
match self {
|
||||
Value(ValueImpl::ByteString(byte_string)) => Some(byte_string),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_text_string(self) -> Option<String> {
|
||||
match self {
|
||||
Value(ValueImpl::TextString(text_string)) => Some(text_string),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_array(self) -> Option<Vec<Value>> {
|
||||
match self {
|
||||
Value(ValueImpl::Array(array)) => Some(array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_map(self) -> Option<Vec<(Value, Value)>> {
|
||||
match self {
|
||||
Value(ValueImpl::Map(map)) => Some(map),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_tag(self) -> Option<(u64, Value)> {
|
||||
match self {
|
||||
Value(ValueImpl::Tag(tag, value)) => Some((tag, *value)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_bool(self) -> Option<bool> {
|
||||
match self {
|
||||
Value(ValueImpl::Simple(SimpleValue::FalseValue)) => Some(false),
|
||||
Value(ValueImpl::Simple(SimpleValue::TrueValue)) => Some(true),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_null(self) -> Option<()> {
|
||||
match self {
|
||||
Value(ValueImpl::Simple(SimpleValue::NullValue)) => Some(()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_undefined(self) -> Option<()> {
|
||||
match self {
|
||||
Value(ValueImpl::Simple(SimpleValue::Undefined)) => Some(()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyType {
|
||||
// For simplicity, this only takes i64. Construct directly for the last bit.
|
||||
pub fn integer(int: i64) -> KeyType {
|
||||
if int >= 0 {
|
||||
KeyType::Unsigned(int as u64)
|
||||
} else {
|
||||
KeyType::Negative(int)
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueImpl {
|
||||
/// Return the major type for the [`ValueImpl`].
|
||||
pub fn type_label(&self) -> u8 {
|
||||
// TODO use enum discriminant instead when stable
|
||||
// https://github.com/rust-lang/rust/issues/60553
|
||||
match self {
|
||||
KeyType::Unsigned(_) => 0,
|
||||
KeyType::Negative(_) => 1,
|
||||
KeyType::ByteString(_) => 2,
|
||||
KeyType::TextString(_) => 3,
|
||||
ValueImpl::Unsigned(_) => 0,
|
||||
ValueImpl::Negative(_) => 1,
|
||||
ValueImpl::ByteString(_) => 2,
|
||||
ValueImpl::TextString(_) => 3,
|
||||
ValueImpl::Array(_) => 4,
|
||||
ValueImpl::Map(_) => 5,
|
||||
ValueImpl::Tag(_, _) => 6,
|
||||
ValueImpl::Simple(_) => 7,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for KeyType {
|
||||
fn cmp(&self, other: &KeyType) -> Ordering {
|
||||
use super::values::KeyType::{ByteString, Negative, TextString, Unsigned};
|
||||
impl Ord for ValueImpl {
|
||||
fn cmp(&self, other: &ValueImpl) -> Ordering {
|
||||
use super::values::ValueImpl::{
|
||||
Array, ByteString, Map, Negative, Simple, Tag, TextString, Unsigned,
|
||||
};
|
||||
let self_type_value = self.type_label();
|
||||
let other_type_value = other.type_label();
|
||||
if self_type_value != other_type_value {
|
||||
@@ -109,18 +258,56 @@ impl Ord for KeyType {
|
||||
(Negative(n1), Negative(n2)) => n1.cmp(n2).reverse(),
|
||||
(ByteString(b1), ByteString(b2)) => b1.len().cmp(&b2.len()).then(b1.cmp(b2)),
|
||||
(TextString(t1), TextString(t2)) => t1.len().cmp(&t2.len()).then(t1.cmp(t2)),
|
||||
_ => unreachable!(),
|
||||
(Array(a1), Array(a2)) if a1.len() != a2.len() => a1.len().cmp(&a2.len()),
|
||||
(Array(a1), Array(a2)) => {
|
||||
// Arrays of same length.
|
||||
let mut ordering = Ordering::Equal;
|
||||
for (e1, e2) in a1.iter().zip(a2.iter()) {
|
||||
ordering = e1.cmp(e2);
|
||||
if !matches!(ordering, Ordering::Equal) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ordering
|
||||
}
|
||||
(Map(m1), Map(m2)) if m1.len() != m2.len() => m1.len().cmp(&m2.len()),
|
||||
(Map(m1), Map(m2)) => {
|
||||
// Maps of same length.
|
||||
let mut ordering = Ordering::Equal;
|
||||
for ((k1, v1), (k2, v2)) in m1.iter().zip(m2.iter()) {
|
||||
ordering = k1.cmp(k2).then_with(|| v1.cmp(v2));
|
||||
if !matches!(ordering, Ordering::Equal) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ordering
|
||||
}
|
||||
(Tag(t1, v1), Tag(t2, v2)) => t1.cmp(t2).then(v1.cmp(v2)),
|
||||
(Simple(s1), Simple(s2)) => s1.cmp(s2),
|
||||
(_, _) => {
|
||||
// The case of different major types is caught above.
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for KeyType {
|
||||
fn partial_cmp(&self, other: &KeyType) -> Option<Ordering> {
|
||||
impl PartialOrd for ValueImpl {
|
||||
fn partial_cmp(&self, other: &ValueImpl) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ValueImpl {}
|
||||
|
||||
impl PartialEq for ValueImpl {
|
||||
fn eq(&self, other: &ValueImpl) -> bool {
|
||||
self.cmp(other) == Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl SimpleValue {
|
||||
/// Create a simple value from its encoded value.
|
||||
pub fn from_integer(int: u64) -> Option<SimpleValue> {
|
||||
match int {
|
||||
20 => Some(SimpleValue::FalseValue),
|
||||
@@ -132,54 +319,93 @@ impl SimpleValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for KeyType {
|
||||
fn from(unsigned: u64) -> Self {
|
||||
KeyType::Unsigned(unsigned)
|
||||
impl From<u64> for Value {
|
||||
fn from(u: u64) -> Self {
|
||||
Value::unsigned(u)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for KeyType {
|
||||
impl From<u32> for Value {
|
||||
fn from(u: u32) -> Self {
|
||||
Value::unsigned(u as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for Value {
|
||||
fn from(u: u16) -> Self {
|
||||
Value::unsigned(u as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Value {
|
||||
fn from(u: u8) -> Self {
|
||||
Value::unsigned(u as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Value {
|
||||
fn from(i: i64) -> Self {
|
||||
KeyType::integer(i)
|
||||
Value::integer(i)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for KeyType {
|
||||
impl From<i32> for Value {
|
||||
fn from(i: i32) -> Self {
|
||||
KeyType::integer(i as i64)
|
||||
Value::integer(i as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for KeyType {
|
||||
impl From<i16> for Value {
|
||||
fn from(i: i16) -> Self {
|
||||
Value::integer(i as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i8> for Value {
|
||||
fn from(i: i8) -> Self {
|
||||
Value::integer(i as i64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Value {
|
||||
fn from(bytes: Vec<u8>) -> Self {
|
||||
KeyType::ByteString(bytes)
|
||||
Value(ValueImpl::ByteString(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for KeyType {
|
||||
impl From<&[u8]> for Value {
|
||||
fn from(bytes: &[u8]) -> Self {
|
||||
KeyType::ByteString(bytes.to_vec())
|
||||
Value(ValueImpl::ByteString(bytes.to_vec()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for KeyType {
|
||||
impl From<&[u8; 0]> for Value {
|
||||
fn from(bytes: &[u8; 0]) -> Self {
|
||||
Value(ValueImpl::ByteString(bytes.to_vec()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Value {
|
||||
fn from(text: String) -> Self {
|
||||
KeyType::TextString(text)
|
||||
Value(ValueImpl::TextString(text))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for KeyType {
|
||||
impl From<&str> for Value {
|
||||
fn from(text: &str) -> Self {
|
||||
KeyType::TextString(text.to_string())
|
||||
Value(ValueImpl::TextString(text.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Value
|
||||
where
|
||||
KeyType: From<T>,
|
||||
{
|
||||
fn from(t: T) -> Self {
|
||||
Value::KeyValue(KeyType::from(t))
|
||||
impl From<Vec<Value>> for Value {
|
||||
fn from(array: Vec<Value>) -> Self {
|
||||
Value(ValueImpl::Array(array))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<(Value, Value)>> for Value {
|
||||
fn from(map: Vec<(Value, Value)>) -> Self {
|
||||
Value::map(map)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,20 +415,9 @@ impl From<bool> for Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoCborKey {
|
||||
fn into_cbor_key(self) -> KeyType;
|
||||
}
|
||||
|
||||
impl<T> IntoCborKey for T
|
||||
where
|
||||
KeyType: From<T>,
|
||||
{
|
||||
fn into_cbor_key(self) -> KeyType {
|
||||
KeyType::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that indicates that a type can be converted to a CBOR [`Value`].
|
||||
pub trait IntoCborValue {
|
||||
/// Convert `self` into a CBOR [`Value`], consuming it along the way.
|
||||
fn into_cbor_value(self) -> Value;
|
||||
}
|
||||
|
||||
@@ -215,7 +430,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that indicates that a type can be converted to a CBOR [`Option<Value>`].
|
||||
pub trait IntoCborValueOption {
|
||||
/// Convert `self` into a CBOR [`Option<Value>`], consuming it along the way.
|
||||
fn into_cbor_value_option(self) -> Option<Value>;
|
||||
}
|
||||
|
||||
@@ -239,32 +456,283 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{cbor_key_bytes, cbor_key_int, cbor_key_text};
|
||||
use super::*;
|
||||
use crate::{
|
||||
cbor_array, cbor_bool, cbor_bytes, cbor_bytes_lit, cbor_int, cbor_map, cbor_null,
|
||||
cbor_tagged, cbor_text, cbor_undefined, cbor_unsigned,
|
||||
};
|
||||
use alloc::vec;
|
||||
|
||||
#[test]
|
||||
fn test_key_type_ordering() {
|
||||
assert!(cbor_key_int!(0) < cbor_key_int!(23));
|
||||
assert!(cbor_key_int!(23) < cbor_key_int!(24));
|
||||
assert!(cbor_key_int!(24) < cbor_key_int!(1000));
|
||||
assert!(cbor_key_int!(1000) < cbor_key_int!(1000000));
|
||||
assert!(cbor_key_int!(1000000) < cbor_key_int!(std::i64::MAX));
|
||||
assert!(cbor_key_int!(std::i64::MAX) < cbor_key_int!(-1));
|
||||
assert!(cbor_key_int!(-1) < cbor_key_int!(-23));
|
||||
assert!(cbor_key_int!(-23) < cbor_key_int!(-24));
|
||||
assert!(cbor_key_int!(-24) < cbor_key_int!(-1000));
|
||||
assert!(cbor_key_int!(-1000) < cbor_key_int!(-1000000));
|
||||
assert!(cbor_key_int!(-1000000) < cbor_key_int!(std::i64::MIN));
|
||||
assert!(cbor_key_int!(std::i64::MIN) < cbor_key_bytes!(vec![]));
|
||||
assert!(cbor_key_bytes!(vec![]) < cbor_key_bytes!(vec![0x00]));
|
||||
assert!(cbor_key_bytes!(vec![0x00]) < cbor_key_bytes!(vec![0x01]));
|
||||
assert!(cbor_key_bytes!(vec![0x01]) < cbor_key_bytes!(vec![0xFF]));
|
||||
assert!(cbor_key_bytes!(vec![0xFF]) < cbor_key_bytes!(vec![0x00, 0x00]));
|
||||
assert!(cbor_key_bytes!(vec![0x00, 0x00]) < cbor_key_text!(""));
|
||||
assert!(cbor_key_text!("") < cbor_key_text!("a"));
|
||||
assert!(cbor_key_text!("a") < cbor_key_text!("b"));
|
||||
assert!(cbor_key_text!("b") < cbor_key_text!("aa"));
|
||||
assert!(cbor_key_int!(1) < cbor_key_bytes!(vec![0x00]));
|
||||
assert!(cbor_key_int!(1) < cbor_key_text!("s"));
|
||||
assert!(cbor_key_int!(-1) < cbor_key_text!("s"));
|
||||
#[should_panic]
|
||||
fn test_duplicate_map_key() {
|
||||
let _map = cbor_map! {
|
||||
0 => "a",
|
||||
-1 => "c",
|
||||
b"a" => "e",
|
||||
"c" => "g",
|
||||
0 => "b",
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_unsigned() {
|
||||
assert_eq!(cbor_int!(1).extract_unsigned(), Some(1));
|
||||
assert_eq!(cbor_int!(-1).extract_unsigned(), None);
|
||||
assert_eq!(cbor_bytes!(vec![]).extract_unsigned(), None);
|
||||
assert_eq!(cbor_text!("").extract_unsigned(), None);
|
||||
assert_eq!(cbor_array![].extract_unsigned(), None);
|
||||
assert_eq!(cbor_map! {}.extract_unsigned(), None);
|
||||
assert_eq!(cbor_tagged!(1, cbor_text!("s")).extract_unsigned(), None);
|
||||
assert_eq!(cbor_bool!(false).extract_unsigned(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_unsigned_limits() {
|
||||
assert_eq!(
|
||||
cbor_unsigned!(core::u64::MAX).extract_unsigned(),
|
||||
Some(core::u64::MAX)
|
||||
);
|
||||
assert_eq!(
|
||||
cbor_unsigned!((core::i64::MAX as u64) + 1).extract_unsigned(),
|
||||
Some((core::i64::MAX as u64) + 1)
|
||||
);
|
||||
assert_eq!(
|
||||
cbor_int!(core::i64::MAX).extract_unsigned(),
|
||||
Some(core::i64::MAX as u64)
|
||||
);
|
||||
assert_eq!(cbor_int!(123).extract_unsigned(), Some(123));
|
||||
assert_eq!(cbor_int!(0).extract_unsigned(), Some(0));
|
||||
assert_eq!(cbor_int!(-123).extract_unsigned(), None);
|
||||
assert_eq!(cbor_int!(core::i64::MIN).extract_unsigned(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_integer() {
|
||||
assert_eq!(cbor_int!(1).extract_integer(), Some(1));
|
||||
assert_eq!(cbor_int!(-1).extract_integer(), Some(-1));
|
||||
assert_eq!(cbor_bytes!(vec![]).extract_integer(), None);
|
||||
assert_eq!(cbor_text!("").extract_integer(), None);
|
||||
assert_eq!(cbor_array![].extract_integer(), None);
|
||||
assert_eq!(cbor_map! {}.extract_integer(), None);
|
||||
assert_eq!(cbor_tagged!(1, cbor_text!("s")).extract_integer(), None);
|
||||
assert_eq!(cbor_bool!(false).extract_integer(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_integer_limits() {
|
||||
assert_eq!(cbor_unsigned!(core::u64::MAX).extract_integer(), None);
|
||||
assert_eq!(
|
||||
cbor_unsigned!((core::i64::MAX as u64) + 1).extract_integer(),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
cbor_int!(core::i64::MAX).extract_integer(),
|
||||
Some(core::i64::MAX)
|
||||
);
|
||||
assert_eq!(cbor_int!(123).extract_integer(), Some(123));
|
||||
assert_eq!(cbor_int!(0).extract_integer(), Some(0));
|
||||
assert_eq!(cbor_int!(-123).extract_integer(), Some(-123));
|
||||
assert_eq!(
|
||||
cbor_int!(core::i64::MIN).extract_integer(),
|
||||
Some(core::i64::MIN)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_byte_string() {
|
||||
assert_eq!(cbor_int!(1).extract_byte_string(), None);
|
||||
assert_eq!(cbor_int!(-1).extract_byte_string(), None);
|
||||
assert_eq!(cbor_bytes!(vec![]).extract_byte_string(), Some(Vec::new()));
|
||||
assert_eq!(
|
||||
cbor_bytes_lit!(b"bar").extract_byte_string(),
|
||||
Some(b"bar".to_vec())
|
||||
);
|
||||
assert_eq!(cbor_text!("").extract_byte_string(), None);
|
||||
assert_eq!(cbor_array![].extract_byte_string(), None);
|
||||
assert_eq!(cbor_map! {}.extract_byte_string(), None);
|
||||
assert_eq!(cbor_tagged!(1, cbor_text!("s")).extract_byte_string(), None);
|
||||
assert_eq!(cbor_bool!(false).extract_byte_string(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_text_string() {
|
||||
assert_eq!(cbor_int!(1).extract_text_string(), None);
|
||||
assert_eq!(cbor_int!(-1).extract_text_string(), None);
|
||||
assert_eq!(cbor_bytes!(vec![]).extract_text_string(), None);
|
||||
assert_eq!(cbor_text!("").extract_text_string(), Some(String::new()));
|
||||
assert_eq!(cbor_text!("s").extract_text_string(), Some("s".to_string()));
|
||||
assert_eq!(cbor_array![].extract_text_string(), None);
|
||||
assert_eq!(cbor_map! {}.extract_text_string(), None);
|
||||
assert_eq!(cbor_tagged!(1, cbor_text!("s")).extract_text_string(), None);
|
||||
assert_eq!(cbor_bool!(false).extract_text_string(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_array() {
|
||||
assert_eq!(cbor_int!(1).extract_array(), None);
|
||||
assert_eq!(cbor_int!(-1).extract_array(), None);
|
||||
assert_eq!(cbor_bytes!(vec![]).extract_array(), None);
|
||||
assert_eq!(cbor_text!("").extract_array(), None);
|
||||
assert_eq!(cbor_array![].extract_array(), Some(Vec::new()));
|
||||
assert_eq!(
|
||||
cbor_array![cbor_int!(1)].extract_array(),
|
||||
Some(vec![cbor_int!(1)])
|
||||
);
|
||||
assert_eq!(cbor_map! {}.extract_array(), None);
|
||||
assert_eq!(cbor_tagged!(1, cbor_text!("s")).extract_array(), None);
|
||||
assert_eq!(cbor_bool!(false).extract_array(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_map() {
|
||||
assert_eq!(cbor_int!(1).extract_map(), None);
|
||||
assert_eq!(cbor_int!(-1).extract_map(), None);
|
||||
assert_eq!(cbor_bytes!(vec![]).extract_map(), None);
|
||||
assert_eq!(cbor_text!("").extract_map(), None);
|
||||
assert_eq!(cbor_array![].extract_map(), None);
|
||||
assert_eq!(cbor_map! {}.extract_map(), Some(Vec::new()));
|
||||
assert_eq!(
|
||||
cbor_map! {0 => 1}.extract_map(),
|
||||
Some(vec![(cbor_int!(0), cbor_int!(1))])
|
||||
);
|
||||
assert_eq!(cbor_tagged!(1, cbor_text!("s")).extract_map(), None);
|
||||
assert_eq!(cbor_bool!(false).extract_map(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_tag() {
|
||||
assert_eq!(cbor_int!(1).extract_tag(), None);
|
||||
assert_eq!(cbor_int!(-1).extract_tag(), None);
|
||||
assert_eq!(cbor_bytes!(vec![]).extract_tag(), None);
|
||||
assert_eq!(cbor_text!("").extract_tag(), None);
|
||||
assert_eq!(cbor_array![].extract_tag(), None);
|
||||
assert_eq!(cbor_map! {}.extract_tag(), None);
|
||||
assert_eq!(
|
||||
cbor_tagged!(1, cbor_text!("s")).extract_tag(),
|
||||
Some((1, cbor_text!("s")))
|
||||
);
|
||||
assert_eq!(cbor_bool!(false).extract_tag(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_bool() {
|
||||
assert_eq!(cbor_int!(1).extract_bool(), None);
|
||||
assert_eq!(cbor_int!(-1).extract_bool(), None);
|
||||
assert_eq!(cbor_bytes!(vec![]).extract_bool(), None);
|
||||
assert_eq!(cbor_text!("").extract_bool(), None);
|
||||
assert_eq!(cbor_array![].extract_bool(), None);
|
||||
assert_eq!(cbor_map! {}.extract_bool(), None);
|
||||
assert_eq!(cbor_tagged!(1, cbor_text!("s")).extract_bool(), None);
|
||||
assert_eq!(cbor_bool!(false).extract_bool(), Some(false));
|
||||
assert_eq!(cbor_bool!(true).extract_bool(), Some(true));
|
||||
assert_eq!(cbor_null!().extract_bool(), None);
|
||||
assert_eq!(cbor_undefined!().extract_bool(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_null() {
|
||||
assert_eq!(cbor_int!(1).extract_null(), None);
|
||||
assert_eq!(cbor_int!(-1).extract_null(), None);
|
||||
assert_eq!(cbor_bytes!(vec![]).extract_null(), None);
|
||||
assert_eq!(cbor_text!("").extract_null(), None);
|
||||
assert_eq!(cbor_array![].extract_null(), None);
|
||||
assert_eq!(cbor_map! {}.extract_null(), None);
|
||||
assert_eq!(cbor_tagged!(1, cbor_text!("s")).extract_null(), None);
|
||||
assert_eq!(cbor_bool!(false).extract_null(), None);
|
||||
assert_eq!(cbor_bool!(true).extract_null(), None);
|
||||
assert_eq!(cbor_null!().extract_null(), Some(()));
|
||||
assert_eq!(cbor_undefined!().extract_null(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_undefined() {
|
||||
assert_eq!(cbor_int!(1).extract_undefined(), None);
|
||||
assert_eq!(cbor_int!(-1).extract_undefined(), None);
|
||||
assert_eq!(cbor_bytes!(vec![]).extract_undefined(), None);
|
||||
assert_eq!(cbor_text!("").extract_undefined(), None);
|
||||
assert_eq!(cbor_array![].extract_undefined(), None);
|
||||
assert_eq!(cbor_map! {}.extract_undefined(), None);
|
||||
assert_eq!(cbor_tagged!(1, cbor_text!("s")).extract_undefined(), None);
|
||||
assert_eq!(cbor_bool!(false).extract_undefined(), None);
|
||||
assert_eq!(cbor_bool!(true).extract_undefined(), None);
|
||||
assert_eq!(cbor_null!().extract_undefined(), None);
|
||||
assert_eq!(cbor_undefined!().extract_undefined(), Some(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value_ordering() {
|
||||
assert!(cbor_int!(0) < cbor_int!(23));
|
||||
assert!(cbor_int!(23) < cbor_int!(24));
|
||||
assert!(cbor_int!(24) < cbor_int!(1000));
|
||||
assert!(cbor_int!(1000) < cbor_int!(1000000));
|
||||
assert!(cbor_int!(1000000) < cbor_int!(core::i64::MAX));
|
||||
assert!(cbor_int!(core::i64::MAX) < cbor_int!(-1));
|
||||
assert!(cbor_int!(-1) < cbor_int!(-23));
|
||||
assert!(cbor_int!(-23) < cbor_int!(-24));
|
||||
assert!(cbor_int!(-24) < cbor_int!(-1000));
|
||||
assert!(cbor_int!(-1000) < cbor_int!(-1000000));
|
||||
assert!(cbor_int!(-1000000) < cbor_int!(core::i64::MIN));
|
||||
assert!(cbor_int!(core::i64::MIN) < cbor_bytes!(vec![]));
|
||||
assert!(cbor_bytes!(vec![]) < cbor_bytes!(vec![0x00]));
|
||||
assert!(cbor_bytes!(vec![0x00]) < cbor_bytes!(vec![0x01]));
|
||||
assert!(cbor_bytes!(vec![0x01]) < cbor_bytes!(vec![0xFF]));
|
||||
assert!(cbor_bytes!(vec![0xFF]) < cbor_bytes!(vec![0x00, 0x00]));
|
||||
assert!(cbor_bytes!(vec![0x00, 0x00]) < cbor_text!(""));
|
||||
assert!(cbor_text!("") < cbor_text!("a"));
|
||||
assert!(cbor_text!("a") < cbor_text!("b"));
|
||||
assert!(cbor_text!("b") < cbor_text!("aa"));
|
||||
assert!(cbor_text!("aa") < cbor_array![]);
|
||||
assert!(cbor_array![] < cbor_array![0]);
|
||||
assert!(cbor_array![0] < cbor_array![-1]);
|
||||
assert!(cbor_array![1] < cbor_array![b""]);
|
||||
assert!(cbor_array![b""] < cbor_array![""]);
|
||||
assert!(cbor_array![""] < cbor_array![cbor_array![]]);
|
||||
assert!(cbor_array![cbor_array![]] < cbor_array![cbor_map! {}]);
|
||||
assert!(cbor_array![cbor_map! {}] < cbor_array![false]);
|
||||
assert!(cbor_array![false] < cbor_array![0, 0]);
|
||||
assert!(cbor_array![0, 0] < cbor_map! {});
|
||||
assert!(cbor_map! {} < cbor_map! {0 => 0});
|
||||
assert!(cbor_map! {0 => 0} < cbor_map! {0 => 1});
|
||||
assert!(cbor_map! {0 => 1} < cbor_map! {1 => 0});
|
||||
assert!(cbor_map! {1 => 0} < cbor_map! {-1 => 0});
|
||||
assert!(cbor_map! {-1 => 0} < cbor_map! {b"" => 0});
|
||||
assert!(cbor_map! {b"" => 0} < cbor_map! {"" => 0});
|
||||
assert!(cbor_map! {"" => 0} < cbor_map! {cbor_array![] => 0});
|
||||
assert!(cbor_map! {cbor_array![] => 0} < cbor_map! {cbor_map!{} => 0});
|
||||
assert!(cbor_map! {cbor_map!{} => 0} < cbor_map! {false => 0});
|
||||
assert!(cbor_map! {false => 0} < cbor_map! {0 => 0, 1 => 0});
|
||||
assert!(cbor_map! {0 => 0} < cbor_tagged!(2, cbor_int!(0)));
|
||||
assert!(cbor_map! {0 => 0, 1 => 0} < cbor_bool!(false));
|
||||
assert!(cbor_bool!(false) < cbor_bool!(true));
|
||||
assert!(cbor_bool!(true) < cbor_null!());
|
||||
assert!(cbor_null!() < cbor_undefined!());
|
||||
assert!(cbor_tagged!(1, cbor_text!("s")) < cbor_tagged!(2, cbor_int!(0)));
|
||||
assert!(cbor_int!(1) < cbor_int!(-1));
|
||||
assert!(cbor_int!(1) < cbor_bytes!(vec![0x00]));
|
||||
assert!(cbor_int!(1) < cbor_text!("s"));
|
||||
assert!(cbor_int!(1) < cbor_array![]);
|
||||
assert!(cbor_int!(1) < cbor_map! {});
|
||||
assert!(cbor_int!(1) < cbor_tagged!(1, cbor_text!("s")));
|
||||
assert!(cbor_int!(1) < cbor_bool!(false));
|
||||
assert!(cbor_int!(-1) < cbor_bytes!(vec![0x00]));
|
||||
assert!(cbor_int!(-1) < cbor_text!("s"));
|
||||
assert!(cbor_int!(-1) < cbor_array![]);
|
||||
assert!(cbor_int!(-1) < cbor_map! {});
|
||||
assert!(cbor_int!(-1) < cbor_tagged!(1, cbor_text!("s")));
|
||||
assert!(cbor_int!(-1) < cbor_bool!(false));
|
||||
assert!(cbor_bytes!(vec![0x00]) < cbor_text!("s"));
|
||||
assert!(cbor_bytes!(vec![0x00]) < cbor_array![]);
|
||||
assert!(cbor_bytes!(vec![0x00]) < cbor_map! {});
|
||||
assert!(cbor_bytes!(vec![0x00]) < cbor_tagged!(1, cbor_text!("s")));
|
||||
assert!(cbor_bytes!(vec![0x00]) < cbor_bool!(false));
|
||||
assert!(cbor_text!("s") < cbor_array![]);
|
||||
assert!(cbor_text!("s") < cbor_map! {});
|
||||
assert!(cbor_text!("s") < cbor_tagged!(1, cbor_text!("s")));
|
||||
assert!(cbor_text!("s") < cbor_bool!(false));
|
||||
assert!(cbor_array![] < cbor_map!(0 => 1));
|
||||
assert!(cbor_array![] < cbor_tagged!(2, cbor_int!(0)));
|
||||
assert!(cbor_array![] < cbor_bool!(false));
|
||||
assert!(cbor_tagged!(1, cbor_text!("s")) < cbor_bool!(false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,35 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::values::{Constants, KeyType, Value};
|
||||
//! Functionality for serializing CBOR values into bytes.
|
||||
|
||||
use super::values::{Constants, Value, ValueImpl};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub fn write(value: Value, encoded_cbor: &mut Vec<u8>) -> bool {
|
||||
/// Possible errors from a serialization operation.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum EncoderError {
|
||||
TooMuchNesting,
|
||||
DuplicateMapKey,
|
||||
}
|
||||
|
||||
/// Convert a [`Value`] to serialized CBOR data, consuming it along the way and appending to the provided vector.
|
||||
/// Maximum level of nesting supported is 127; more deeply nested structures will fail with
|
||||
/// [`EncoderError::TooMuchNesting`].
|
||||
pub fn write(value: Value, encoded_cbor: &mut Vec<u8>) -> Result<(), EncoderError> {
|
||||
write_nested(value, encoded_cbor, Some(i8::MAX))
|
||||
}
|
||||
|
||||
/// Convert a [`Value`] to serialized CBOR data, consuming it along the way and appending to the provided vector. If
|
||||
/// `max_nest` is `Some(max)`, then nested structures are only supported up to the given limit (returning
|
||||
/// [`DecoderError::TooMuchNesting`] if the limit is hit).
|
||||
pub fn write_nested(
|
||||
value: Value,
|
||||
encoded_cbor: &mut Vec<u8>,
|
||||
max_nest: Option<i8>,
|
||||
) -> Result<(), EncoderError> {
|
||||
let mut writer = Writer::new(encoded_cbor);
|
||||
writer.encode_cbor(value, Writer::MAX_NESTING_DEPTH)
|
||||
writer.encode_cbor(value, max_nest)
|
||||
}
|
||||
|
||||
struct Writer<'a> {
|
||||
@@ -25,51 +48,50 @@ struct Writer<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Writer<'a> {
|
||||
const MAX_NESTING_DEPTH: i8 = 4;
|
||||
|
||||
pub fn new(encoded_cbor: &mut Vec<u8>) -> Writer {
|
||||
Writer { encoded_cbor }
|
||||
}
|
||||
|
||||
fn encode_cbor(&mut self, value: Value, remaining_depth: i8) -> bool {
|
||||
if remaining_depth < 0 {
|
||||
return false;
|
||||
fn encode_cbor(
|
||||
&mut self,
|
||||
value: Value,
|
||||
remaining_depth: Option<i8>,
|
||||
) -> Result<(), EncoderError> {
|
||||
if remaining_depth.map_or(false, |d| d < 0) {
|
||||
return Err(EncoderError::TooMuchNesting);
|
||||
}
|
||||
match value {
|
||||
Value::KeyValue(KeyType::Unsigned(unsigned)) => self.start_item(0, unsigned),
|
||||
Value::KeyValue(KeyType::Negative(negative)) => {
|
||||
self.start_item(1, -(negative + 1) as u64)
|
||||
}
|
||||
Value::KeyValue(KeyType::ByteString(byte_string)) => {
|
||||
self.start_item(2, byte_string.len() as u64);
|
||||
let type_label = value.0.type_label();
|
||||
match value.0 {
|
||||
ValueImpl::Unsigned(unsigned) => self.start_item(type_label, unsigned),
|
||||
ValueImpl::Negative(negative) => self.start_item(type_label, -(negative + 1) as u64),
|
||||
ValueImpl::ByteString(byte_string) => {
|
||||
self.start_item(type_label, byte_string.len() as u64);
|
||||
self.encoded_cbor.extend(byte_string);
|
||||
}
|
||||
Value::KeyValue(KeyType::TextString(text_string)) => {
|
||||
self.start_item(3, text_string.len() as u64);
|
||||
ValueImpl::TextString(text_string) => {
|
||||
self.start_item(type_label, text_string.len() as u64);
|
||||
self.encoded_cbor.extend(text_string.into_bytes());
|
||||
}
|
||||
Value::Array(array) => {
|
||||
self.start_item(4, array.len() as u64);
|
||||
ValueImpl::Array(array) => {
|
||||
self.start_item(type_label, array.len() as u64);
|
||||
for el in array {
|
||||
if !self.encode_cbor(el, remaining_depth - 1) {
|
||||
return false;
|
||||
}
|
||||
self.encode_cbor(el, remaining_depth.map(|d| d - 1))?;
|
||||
}
|
||||
}
|
||||
Value::Map(map) => {
|
||||
self.start_item(5, map.len() as u64);
|
||||
ValueImpl::Map(map) => {
|
||||
self.start_item(type_label, map.len() as u64);
|
||||
for (k, v) in map {
|
||||
if !self.encode_cbor(Value::KeyValue(k), remaining_depth - 1) {
|
||||
return false;
|
||||
}
|
||||
if !self.encode_cbor(v, remaining_depth - 1) {
|
||||
return false;
|
||||
}
|
||||
self.encode_cbor(k, remaining_depth.map(|d| d - 1))?;
|
||||
self.encode_cbor(v, remaining_depth.map(|d| d - 1))?;
|
||||
}
|
||||
}
|
||||
Value::Simple(simple_value) => self.start_item(7, simple_value as u64),
|
||||
ValueImpl::Tag(tag, inner_value) => {
|
||||
self.start_item(type_label, tag);
|
||||
self.encode_cbor(*inner_value, remaining_depth.map(|d| d - 1))?;
|
||||
}
|
||||
ValueImpl::Simple(simple_value) => self.start_item(type_label, simple_value as u64),
|
||||
}
|
||||
true
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start_item(&mut self, type_label: u8, size: u64) {
|
||||
@@ -94,12 +116,13 @@ mod test {
|
||||
use super::*;
|
||||
use crate::{
|
||||
cbor_array, cbor_array_vec, cbor_bytes, cbor_false, cbor_int, cbor_map, cbor_null,
|
||||
cbor_text, cbor_true, cbor_undefined,
|
||||
cbor_tagged, cbor_text, cbor_true, cbor_undefined,
|
||||
};
|
||||
use alloc::vec;
|
||||
|
||||
fn write_return(value: Value) -> Option<Vec<u8>> {
|
||||
let mut encoded_cbor = Vec::new();
|
||||
if write(value, &mut encoded_cbor) {
|
||||
if write(value, &mut encoded_cbor).is_ok() {
|
||||
Some(encoded_cbor)
|
||||
} else {
|
||||
None
|
||||
@@ -124,7 +147,7 @@ mod test {
|
||||
vec![0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00],
|
||||
),
|
||||
(
|
||||
std::i64::MAX,
|
||||
core::i64::MAX,
|
||||
vec![0x1B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
|
||||
),
|
||||
];
|
||||
@@ -149,7 +172,7 @@ mod test {
|
||||
vec![0x3B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00],
|
||||
),
|
||||
(
|
||||
std::i64::MIN,
|
||||
core::i64::MIN,
|
||||
vec![0x3B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
|
||||
),
|
||||
];
|
||||
@@ -182,7 +205,7 @@ mod test {
|
||||
("\"\\", vec![0x62, 0x22, 0x5C]),
|
||||
("ü", vec![0x62, 0xC3, 0xBC]),
|
||||
(
|
||||
std::str::from_utf8(&unicode_3byte).unwrap(),
|
||||
core::str::from_utf8(&unicode_3byte).unwrap(),
|
||||
vec![0x63, 0xE6, 0xB0, 0xB4],
|
||||
),
|
||||
("𐅑", vec![0x64, 0xF0, 0x90, 0x85, 0x91]),
|
||||
@@ -209,9 +232,16 @@ mod test {
|
||||
#[test]
|
||||
fn test_write_map() {
|
||||
let value_map = cbor_map! {
|
||||
"aa" => "AA",
|
||||
"e" => "E",
|
||||
"" => ".",
|
||||
0 => "a",
|
||||
23 => "b",
|
||||
24 => "c",
|
||||
core::u8::MAX as i64 => "d",
|
||||
256 => "e",
|
||||
core::u16::MAX as i64 => "f",
|
||||
65536 => "g",
|
||||
core::u32::MAX as i64 => "h",
|
||||
4294967296_i64 => "i",
|
||||
core::i64::MAX => "j",
|
||||
-1 => "k",
|
||||
-24 => "l",
|
||||
-25 => "m",
|
||||
@@ -220,20 +250,13 @@ mod test {
|
||||
-65537 => "p",
|
||||
-4294967296_i64 => "q",
|
||||
-4294967297_i64 => "r",
|
||||
std::i64::MIN => "s",
|
||||
core::i64::MIN => "s",
|
||||
b"a" => 2,
|
||||
b"bar" => 3,
|
||||
b"foo" => 4,
|
||||
0 => "a",
|
||||
23 => "b",
|
||||
24 => "c",
|
||||
std::u8::MAX as i64 => "d",
|
||||
256 => "e",
|
||||
std::u16::MAX as i64 => "f",
|
||||
65536 => "g",
|
||||
std::u32::MAX as i64 => "h",
|
||||
4294967296_i64 => "i",
|
||||
std::i64::MAX => "j",
|
||||
"" => ".",
|
||||
"e" => "E",
|
||||
"aa" => "AA",
|
||||
};
|
||||
let expected_cbor = vec![
|
||||
0xb8, 0x19, // map of 25 pairs:
|
||||
@@ -288,6 +311,31 @@ mod test {
|
||||
assert_eq!(write_return(value_map), Some(expected_cbor));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_map_sorted() {
|
||||
let sorted_map = cbor_map! {
|
||||
0 => "a",
|
||||
1 => "b",
|
||||
-1 => "c",
|
||||
-2 => "d",
|
||||
b"a" => "e",
|
||||
b"b" => "f",
|
||||
"" => "g",
|
||||
"c" => "h",
|
||||
};
|
||||
let unsorted_map = cbor_map! {
|
||||
1 => "b",
|
||||
-2 => "d",
|
||||
b"b" => "f",
|
||||
"c" => "h",
|
||||
"" => "g",
|
||||
b"a" => "e",
|
||||
-1 => "c",
|
||||
0 => "a",
|
||||
};
|
||||
assert_eq!(write_return(sorted_map), write_return(unsorted_map));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_map_with_array() {
|
||||
let value_map = cbor_map! {
|
||||
@@ -325,6 +373,33 @@ mod test {
|
||||
assert_eq!(write_return(value_map), Some(expected_cbor));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_tagged() {
|
||||
let cases = vec![
|
||||
(cbor_tagged!(6, cbor_int!(0x42)), vec![0xc6, 0x18, 0x42]),
|
||||
(cbor_tagged!(1, cbor_true!()), vec![0xc1, 0xf5]),
|
||||
(
|
||||
cbor_tagged!(
|
||||
1000,
|
||||
cbor_map! {
|
||||
"a" => 1,
|
||||
"b" => cbor_array![2, 3],
|
||||
}
|
||||
),
|
||||
vec![
|
||||
0xd9, 0x03, 0xe8, 0xa2, // map of 2 pairs
|
||||
0x61, 0x61, // "a"
|
||||
0x01, 0x61, 0x62, // "b"
|
||||
0x82, // array with 2 elements
|
||||
0x02, 0x03,
|
||||
],
|
||||
),
|
||||
];
|
||||
for (value, correct_cbor) in cases {
|
||||
assert_eq!(write_return(value), Some(correct_cbor));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_simple() {
|
||||
let cases = vec![
|
||||
@@ -355,12 +430,12 @@ mod test {
|
||||
for (value, level) in positive_cases {
|
||||
let mut buf = Vec::new();
|
||||
let mut writer = Writer::new(&mut buf);
|
||||
assert!(writer.encode_cbor(value, level));
|
||||
assert!(writer.encode_cbor(value, Some(level)).is_ok());
|
||||
}
|
||||
for (value, level) in negative_cases {
|
||||
let mut buf = Vec::new();
|
||||
let mut writer = Writer::new(&mut buf);
|
||||
assert!(!writer.encode_cbor(value, level));
|
||||
assert!(!writer.encode_cbor(value, Some(level)).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,9 +451,10 @@ mod test {
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let mut writer = Writer::new(&mut buf);
|
||||
assert!(writer.encode_cbor(cbor_map.clone(), 2));
|
||||
assert!(writer.encode_cbor(cbor_map.clone(), Some(2)).is_ok());
|
||||
assert!(writer.encode_cbor(cbor_map.clone(), None).is_ok());
|
||||
writer = Writer::new(&mut buf);
|
||||
assert!(!writer.encode_cbor(cbor_map, 1));
|
||||
assert!(writer.encode_cbor(cbor_map, Some(1)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -398,9 +474,9 @@ mod test {
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let mut writer = Writer::new(&mut buf);
|
||||
assert!(writer.encode_cbor(cbor_array.clone(), 3));
|
||||
assert!(writer.encode_cbor(cbor_array.clone(), Some(3)).is_ok());
|
||||
writer = Writer::new(&mut buf);
|
||||
assert!(!writer.encode_cbor(cbor_array, 2));
|
||||
assert!(writer.encode_cbor(cbor_array, Some(2)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -426,8 +502,9 @@ mod test {
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let mut writer = Writer::new(&mut buf);
|
||||
assert!(writer.encode_cbor(cbor_map.clone(), 5));
|
||||
assert!(writer.encode_cbor(cbor_map.clone(), Some(5)).is_ok());
|
||||
assert!(writer.encode_cbor(cbor_map.clone(), None).is_ok());
|
||||
writer = Writer::new(&mut buf);
|
||||
assert!(!writer.encode_cbor(cbor_map, 4));
|
||||
assert!(writer.encode_cbor(cbor_map, Some(4)).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
492
libraries/crypto/Cargo.lock
generated
492
libraries/crypto/Cargo.lock
generated
@@ -1,492 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cbor"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"byteorder",
|
||||
"cbor",
|
||||
"hex",
|
||||
"libtock_drivers",
|
||||
"rand",
|
||||
"regex",
|
||||
"ring",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"subtle",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.134"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
|
||||
|
||||
[[package]]
|
||||
name = "libtock_codegen"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtock_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libtock_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtock_drivers"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libtock_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||
dependencies = [
|
||||
"autocfg 0.1.8",
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core 0.4.2",
|
||||
"rand_hc",
|
||||
"rand_isaac",
|
||||
"rand_jitter",
|
||||
"rand_os",
|
||||
"rand_pcg",
|
||||
"rand_xorshift",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||
dependencies = [
|
||||
"autocfg 0.1.8",
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
dependencies = [
|
||||
"rand_core 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_isaac"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_jitter"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_core 0.4.2",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_os"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||
dependencies = [
|
||||
"cloudabi",
|
||||
"fuchsia-cprng",
|
||||
"libc",
|
||||
"rand_core 0.4.2",
|
||||
"rdrand",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
||||
dependencies = [
|
||||
"autocfg 0.1.8",
|
||||
"rand_core 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rdrand"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
@@ -10,20 +10,17 @@ license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libtock_drivers = { path = "../../third_party/libtock-drivers" }
|
||||
cbor = { path = "../cbor" }
|
||||
arrayref = "0.3.6"
|
||||
subtle = { version = "2.2.3", default-features = false, features = ["nightly"] }
|
||||
byteorder = { version = "1", default-features = false }
|
||||
hex = { version = "0.3.2", default-features = false, optional = true }
|
||||
ring = { version = "0.16.11", optional = true }
|
||||
untrusted = { version = "0.7.0", optional = true }
|
||||
rand = { version = "0.6.5", optional = true }
|
||||
serde = { version = "1.0", optional = true, features = ["derive"] }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
serde_json = { version = "=1.0.69", optional = true }
|
||||
regex = { version = "1", optional = true }
|
||||
rand_core = "0.6.4"
|
||||
zeroize = { version = "1.5.7", features = ["derive"] }
|
||||
|
||||
[features]
|
||||
std = ["cbor/std", "hex", "rand", "ring", "untrusted", "serde", "serde_json", "regex"]
|
||||
derive_debug = []
|
||||
with_ctap1 = []
|
||||
std = ["hex", "ring", "untrusted", "serde", "serde_json", "regex", "rand_core/getrandom"]
|
||||
|
||||
@@ -12,19 +12,29 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::util::{xor_block_16, Block16};
|
||||
use super::{Decrypt16BytesBlock, Encrypt16BytesBlock};
|
||||
use arrayref::{array_mut_ref, array_ref};
|
||||
//! A portable and naive textbook implementation of AES-256
|
||||
|
||||
use super::util::{xor_block_16, Block16};
|
||||
use arrayref::{array_mut_ref, array_ref};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/** A portable and naive textbook implementation of AES-256 **/
|
||||
type Word = [u8; 4];
|
||||
|
||||
/** This structure caches the round keys, to avoid re-computing the key schedule for each block. **/
|
||||
/// Encryption key for AES256.
|
||||
///
|
||||
/// Never call zeroize explicitly, to not invalidate any invariants.
|
||||
#[derive(Zeroize)]
|
||||
pub struct EncryptionKey {
|
||||
// This structure caches the round keys, to avoid re-computing the key schedule for each block.
|
||||
enc_round_keys: [Block16; 15],
|
||||
}
|
||||
|
||||
/// Decryption key for AES256.
|
||||
///
|
||||
/// Never call zeroize explicitly, to not invalidate any invariants.
|
||||
#[derive(Zeroize)]
|
||||
pub struct DecryptionKey {
|
||||
// This structure caches the round keys, to avoid re-computing the key schedule for each block.
|
||||
dec_round_keys: [Block16; 15],
|
||||
}
|
||||
|
||||
@@ -54,11 +64,9 @@ impl EncryptionKey {
|
||||
|
||||
EncryptionKey { enc_round_keys }
|
||||
}
|
||||
}
|
||||
|
||||
impl Encrypt16BytesBlock for EncryptionKey {
|
||||
// Encrypt an AES block in place.
|
||||
fn encrypt_block(&self, block: &mut Block16) {
|
||||
pub fn encrypt_block(&self, block: &mut Block16) {
|
||||
add_round_key(block, &self.enc_round_keys[0]);
|
||||
for i in 1..14 {
|
||||
aes_enc(block, &self.enc_round_keys[i]);
|
||||
@@ -82,11 +90,9 @@ impl DecryptionKey {
|
||||
|
||||
DecryptionKey { dec_round_keys }
|
||||
}
|
||||
}
|
||||
|
||||
impl Decrypt16BytesBlock for DecryptionKey {
|
||||
// Decrypt an AES block in place.
|
||||
fn decrypt_block(&self, block: &mut Block16) {
|
||||
pub fn decrypt_block(&self, block: &mut Block16) {
|
||||
add_round_key(block, &self.dec_round_keys[0]);
|
||||
for i in 1..14 {
|
||||
aes_dec(block, &self.dec_round_keys[i]);
|
||||
|
||||
@@ -13,24 +13,31 @@
|
||||
// limitations under the License.
|
||||
|
||||
use super::util::{xor_block_16, Block16};
|
||||
use super::{Decrypt16BytesBlock, Encrypt16BytesBlock};
|
||||
use crate::aes256::{DecryptionKey, EncryptionKey};
|
||||
use core::convert::TryInto;
|
||||
|
||||
pub fn cbc_encrypt<K>(key: &K, mut iv: Block16, blocks: &mut [Block16])
|
||||
where
|
||||
K: Encrypt16BytesBlock,
|
||||
{
|
||||
for block in blocks {
|
||||
/// Encrypts a byte slice.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the byte slice is not a multiple of the block size (16 bytes).
|
||||
pub fn cbc_encrypt(key: &EncryptionKey, mut iv: Block16, bytes: &mut [u8]) {
|
||||
for block in bytes.chunks_mut(16) {
|
||||
let block: &mut Block16 = block.try_into().unwrap();
|
||||
xor_block_16(block, &iv);
|
||||
key.encrypt_block(block);
|
||||
iv = *block;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cbc_decrypt<K>(key: &K, mut iv: Block16, blocks: &mut [Block16])
|
||||
where
|
||||
K: Decrypt16BytesBlock,
|
||||
{
|
||||
for block in blocks {
|
||||
/// Decrypts a byte slice.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the byte slice is not a multiple of the block size (16 bytes).
|
||||
pub fn cbc_decrypt(key: &DecryptionKey, mut iv: Block16, bytes: &mut [u8]) {
|
||||
for block in bytes.chunks_mut(16) {
|
||||
let block: &mut Block16 = block.try_into().unwrap();
|
||||
let tmp = *block;
|
||||
key.decrypt_block(block);
|
||||
xor_block_16(block, &iv);
|
||||
@@ -54,11 +61,9 @@ mod test {
|
||||
let dec_key = aes256::DecryptionKey::new(&enc_key);
|
||||
|
||||
for len in 0..16 {
|
||||
let mut blocks: Vec<Block16> = vec![Default::default(); len];
|
||||
for i in 0..len {
|
||||
for j in 0..16 {
|
||||
blocks[i][j] = ((len + i) * 16 + j) as u8;
|
||||
}
|
||||
let mut blocks: Vec<u8> = vec![0; 16 * len];
|
||||
for (i, x) in blocks.iter_mut().enumerate() {
|
||||
*x = (16 * len + i) as u8;
|
||||
}
|
||||
let iv = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
@@ -80,10 +85,10 @@ mod test {
|
||||
0x1c, 0x1d, 0x1e, 0x1f,
|
||||
]);
|
||||
|
||||
let mut blocks = [[
|
||||
let mut blocks = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
]];
|
||||
];
|
||||
let iv = [0; 16];
|
||||
cbc_encrypt(&key, iv, &mut blocks);
|
||||
|
||||
@@ -93,7 +98,7 @@ mod test {
|
||||
];
|
||||
key.encrypt_block(&mut expected);
|
||||
|
||||
assert_eq!(blocks, [expected]);
|
||||
assert_eq!(blocks, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -104,10 +109,10 @@ mod test {
|
||||
0x1c, 0x1d, 0x1e, 0x1f,
|
||||
]));
|
||||
|
||||
let mut blocks = [[
|
||||
let mut blocks = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
]];
|
||||
];
|
||||
let iv = [0; 16];
|
||||
cbc_decrypt(&key, iv, &mut blocks);
|
||||
|
||||
@@ -117,7 +122,7 @@ mod test {
|
||||
];
|
||||
key.decrypt_block(&mut expected);
|
||||
|
||||
assert_eq!(blocks, [expected]);
|
||||
assert_eq!(blocks, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -128,10 +133,10 @@ mod test {
|
||||
0x1c, 0x1d, 0x1e, 0x1f,
|
||||
]);
|
||||
|
||||
let mut blocks = [[
|
||||
let mut blocks = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
]];
|
||||
];
|
||||
let iv = [
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
|
||||
0x3e, 0x3f,
|
||||
@@ -145,7 +150,7 @@ mod test {
|
||||
xor_block_16(&mut expected, &iv);
|
||||
key.encrypt_block(&mut expected);
|
||||
|
||||
assert_eq!(blocks, [expected]);
|
||||
assert_eq!(blocks, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -156,10 +161,10 @@ mod test {
|
||||
0x1c, 0x1d, 0x1e, 0x1f,
|
||||
]));
|
||||
|
||||
let mut blocks = [[
|
||||
let mut blocks = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
]];
|
||||
];
|
||||
let iv = [
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
|
||||
0x3e, 0x3f,
|
||||
@@ -173,7 +178,7 @@ mod test {
|
||||
key.decrypt_block(&mut expected);
|
||||
xor_block_16(&mut expected, &iv);
|
||||
|
||||
assert_eq!(blocks, [expected]);
|
||||
assert_eq!(blocks, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -185,14 +190,9 @@ mod test {
|
||||
]);
|
||||
|
||||
let mut blocks = [
|
||||
[
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
],
|
||||
[
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
|
||||
0x4e, 0x4f,
|
||||
],
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
|
||||
0x4c, 0x4d, 0x4e, 0x4f,
|
||||
];
|
||||
let iv = [
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
|
||||
@@ -200,20 +200,20 @@ mod test {
|
||||
];
|
||||
cbc_encrypt(&key, iv, &mut blocks);
|
||||
|
||||
let mut expected0 = [
|
||||
let mut expected = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
];
|
||||
let mut expected1 = [
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
|
||||
0x4e, 0x4f,
|
||||
0x2e, 0x2f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
|
||||
0x4c, 0x4d, 0x4e, 0x4f,
|
||||
];
|
||||
let (expected0, expected1) = expected.split_at_mut(16);
|
||||
let mut expected0: &mut Block16 = expected0.try_into().unwrap();
|
||||
let mut expected1: &mut Block16 = expected1.try_into().unwrap();
|
||||
xor_block_16(&mut expected0, &iv);
|
||||
key.encrypt_block(&mut expected0);
|
||||
xor_block_16(&mut expected1, &expected0);
|
||||
key.encrypt_block(&mut expected1);
|
||||
|
||||
assert_eq!(blocks, [expected0, expected1]);
|
||||
assert_eq!(blocks, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -225,14 +225,9 @@ mod test {
|
||||
]));
|
||||
|
||||
let mut blocks = [
|
||||
[
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
],
|
||||
[
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
|
||||
0x4e, 0x4f,
|
||||
],
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
|
||||
0x4c, 0x4d, 0x4e, 0x4f,
|
||||
];
|
||||
let iv = [
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
|
||||
@@ -240,19 +235,19 @@ mod test {
|
||||
];
|
||||
cbc_decrypt(&key, iv, &mut blocks);
|
||||
|
||||
let mut expected0 = [
|
||||
let mut expected = [
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
|
||||
0x2e, 0x2f,
|
||||
];
|
||||
let mut expected1 = [
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
|
||||
0x4e, 0x4f,
|
||||
0x2e, 0x2f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
|
||||
0x4c, 0x4d, 0x4e, 0x4f,
|
||||
];
|
||||
let (expected0, expected1) = expected.split_at_mut(16);
|
||||
let mut expected0: &mut Block16 = expected0.try_into().unwrap();
|
||||
let mut expected1: &mut Block16 = expected1.try_into().unwrap();
|
||||
key.decrypt_block(&mut expected1);
|
||||
xor_block_16(&mut expected1, &expected0);
|
||||
key.decrypt_block(&mut expected0);
|
||||
xor_block_16(&mut expected0, &iv);
|
||||
|
||||
assert_eq!(blocks, [expected0, expected1]);
|
||||
assert_eq!(blocks, expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,17 +12,19 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::super::rng256::Rng256;
|
||||
use super::int256::{Digit, Int256};
|
||||
use core::ops::Mul;
|
||||
use rand_core::RngCore;
|
||||
use subtle::{self, Choice, ConditionallySelectable, CtOption};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
// An exponent on the elliptic curve, that is an element modulo the curve order N.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
/// An exponent on the elliptic curve, that is an element modulo the curve order N.
|
||||
///
|
||||
/// Never call zeroize explicitly, to not invalidate any invariants.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Zeroize)]
|
||||
// TODO: remove this Default once https://github.com/dalek-cryptography/subtle/issues/63 is
|
||||
// resolved.
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "derive_debug", derive(Debug))]
|
||||
pub struct ExponentP256 {
|
||||
int: Int256,
|
||||
}
|
||||
@@ -91,12 +93,13 @@ impl Mul for &ExponentP256 {
|
||||
}
|
||||
}
|
||||
|
||||
// A non-zero exponent on the elliptic curve.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
/// A non-zero exponent on the elliptic curve.
|
||||
///
|
||||
/// Never call zeroize explicitly, to not invalidate any invariants.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Zeroize)]
|
||||
// TODO: remove this Default once https://github.com/dalek-cryptography/subtle/issues/63 is
|
||||
// resolved.
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "derive_debug", derive(Debug))]
|
||||
pub struct NonZeroExponentP256 {
|
||||
e: ExponentP256,
|
||||
}
|
||||
@@ -114,7 +117,7 @@ impl NonZeroExponentP256 {
|
||||
// Generates a uniformly distributed element 0 < k < N
|
||||
pub fn gen_uniform<R>(r: &mut R) -> NonZeroExponentP256
|
||||
where
|
||||
R: Rng256,
|
||||
R: RngCore,
|
||||
{
|
||||
loop {
|
||||
let x = Int256::gen_uniform_256(r);
|
||||
@@ -295,52 +298,4 @@ pub mod test {
|
||||
assert_eq!(ONE.inv(), ONE);
|
||||
assert_eq!(N_MIN_1.inv(), N_MIN_1);
|
||||
}
|
||||
|
||||
/** RNG **/
|
||||
// Mock rng that samples through a list of values, then panics.
|
||||
struct StressTestingRng {
|
||||
values: Vec<Int256>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl StressTestingRng {
|
||||
pub fn new(values: Vec<Int256>) -> StressTestingRng {
|
||||
StressTestingRng { values, index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Rng256 for StressTestingRng {
|
||||
// This function is unused, as we redefine gen_uniform_u32x8.
|
||||
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn gen_uniform_u32x8(&mut self) -> [u32; 8] {
|
||||
let result = self.values[self.index].digits();
|
||||
self.index += 1;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uniform_non_zero_is_below_n() {
|
||||
let mut rng = StressTestingRng::new(vec![
|
||||
Int256::new([
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff,
|
||||
]),
|
||||
Int256::N,
|
||||
N_MIN_1.to_int(),
|
||||
Int256::N_MIN_2,
|
||||
]);
|
||||
|
||||
assert_eq!(NonZeroExponentP256::gen_uniform(&mut rng), N_MIN_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uniform_n_is_above_zero() {
|
||||
let mut rng = StressTestingRng::new(vec![Int256::ZERO]);
|
||||
|
||||
assert_eq!(NonZeroExponentP256::gen_uniform(&mut rng), ONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,16 @@
|
||||
use super::int256::{Digit, Int256};
|
||||
use core::ops::Mul;
|
||||
use subtle::Choice;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
// A field element on the elliptic curve, that is an element modulo the prime P.
|
||||
// This is the format used to serialize coordinates of points on the curve.
|
||||
// This implements enough methods to validate points and to convert them to/from the Montgomery
|
||||
// form, which is more convenient to operate on.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
/// A field element on the elliptic curve, that is an element modulo the prime P.
|
||||
///
|
||||
/// This is the format used to serialize coordinates of points on the curve.
|
||||
/// This implements enough methods to validate points and to convert them to/from the Montgomery
|
||||
/// form, which is more convenient to operate on.
|
||||
///
|
||||
/// Never call zeroize explicitly, to not invalidate any invariants.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Zeroize)]
|
||||
pub struct GFP256 {
|
||||
int: Int256,
|
||||
}
|
||||
@@ -111,7 +115,6 @@ impl Mul for &GFP256 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "derive_debug")]
|
||||
impl core::fmt::Debug for GFP256 {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "GFP256::{:?}", self.int)
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::super::rng256::Rng256;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use arrayref::{array_mut_ref, array_ref};
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use core::ops::{Add, AddAssign, Sub, SubAssign};
|
||||
use rand_core::RngCore;
|
||||
use subtle::{self, Choice, ConditionallySelectable, ConstantTimeEq};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
const BITS_PER_DIGIT: usize = 32;
|
||||
const BYTES_PER_DIGIT: usize = BITS_PER_DIGIT >> 3;
|
||||
@@ -29,7 +30,10 @@ pub type Digit = u32;
|
||||
type DoubleDigit = u64;
|
||||
type SignedDoubleDigit = i64;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
/// Big integer implementation with 256 bits.
|
||||
///
|
||||
/// Never call zeroize explicitly, to not invalidate any invariants.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Zeroize)]
|
||||
// TODO: remove this Default once https://github.com/dalek-cryptography/subtle/issues/63 is
|
||||
// resolved.
|
||||
#[derive(Default)]
|
||||
@@ -119,11 +123,13 @@ impl Int256 {
|
||||
// Generates a uniformly distributed integer 0 <= x < 2^256
|
||||
pub fn gen_uniform_256<R>(r: &mut R) -> Int256
|
||||
where
|
||||
R: Rng256,
|
||||
R: RngCore,
|
||||
{
|
||||
Int256 {
|
||||
digits: r.gen_uniform_u32x8(),
|
||||
let mut digits = [0; NDIGITS];
|
||||
for i in 0..NDIGITS {
|
||||
digits[i] = r.next_u32();
|
||||
}
|
||||
Int256 { digits }
|
||||
}
|
||||
|
||||
/** Serialization **/
|
||||
@@ -636,7 +642,6 @@ impl SubAssign<&Int256> for Int256 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "derive_debug")]
|
||||
impl core::fmt::Debug for Int256 {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "Int256 {{ digits: {:08x?} }}", self.digits)
|
||||
|
||||
@@ -17,13 +17,16 @@ use super::int256::Int256;
|
||||
use super::precomputed;
|
||||
use core::ops::{Add, Mul, Sub};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
pub const NLIMBS: usize = 9;
|
||||
pub const BOTTOM_28_BITS: u32 = 0x0fff_ffff;
|
||||
pub const BOTTOM_29_BITS: u32 = 0x1fff_ffff;
|
||||
|
||||
/** Field element on the secp256r1 curve, represented in Montgomery form **/
|
||||
#[derive(Clone, Copy)]
|
||||
/// Field element on the secp256r1 curve, represented in Montgomery form.
|
||||
///
|
||||
/// Never call zeroize explicitly, to not invalidate any invariants.
|
||||
#[derive(Clone, Copy, Zeroize)]
|
||||
pub struct Montgomery {
|
||||
// The 9 limbs use 28 or 29 bits, alternatively: even limbs use 29 bits, odd limbs use 28 bits.
|
||||
// The Montgomery form stores a field element x as (x * 2^257) mod P.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user