Compare commits

...

251 Commits
1.0.2 ... 2.1.0

Author SHA1 Message Date
89769ecc18 fix u2f counter for real 2019-03-31 23:29:00 -04:00
3b3f47bfcf Merge pull request #155 from solokeys/pin_lockout_ga
Pin lockout ga
2019-03-29 17:11:48 -04:00
6fa443b0bc tests for GA without pin 2019-03-26 19:00:42 -04:00
893d4131b2 change how pin is enforced for GA 2019-03-26 19:00:12 -04:00
4e21c0bd8f Merge pull request #152 from solokeys/testing_yubikey
Testing yubikey
2019-03-26 18:33:03 -04:00
251eb6bf64 Merge branch 'master' into testing_yubikey 2019-03-26 16:36:08 -04:00
08e236df69 fix code quality issues x/2 2019-03-26 16:14:28 -04:00
d2091563ab fix code quality issues 2019-03-26 16:09:30 -04:00
54a6a82ca0 Merge pull request #153 from solokeys/rng-fix
Fix buffer overrun and use correct size for random u32
2019-03-26 15:37:51 -04:00
40b9dae38a Fix buffer overrun and use correct size for random u32 2019-03-26 01:55:42 +01:00
98a209e330 make target to flash firmware via bootloader 2019-03-23 13:54:04 +01:00
d3b5fb68ee Build debug 1/2 versions of hacker firmware and bundle 2019-03-23 13:52:47 +01:00
74a1f0e21b Merge pull request #143 from solokeys/paranoid-modemmanager
Do not signal "AT modem" for ACM-CDC serial
2019-03-23 01:48:18 -04:00
e21172fff8 Merge pull request #149 from solokeys/hmac-secret
Hmac-secret
2019-03-22 22:05:41 +01:00
9d3144e9b1 oops. black 2019-03-22 21:56:18 +01:00
a2a774125f Fix usage and display fido2-ext in it 2019-03-22 21:40:55 +01:00
349ea5343a Remove paranoid MM stuff, not signaling AT modem is enough + better 2019-03-22 20:01:31 +01:00
c851807376 Do not advertise AT modem capabilities 2019-03-22 20:00:02 +01:00
84d1629aa3 Allow toggling between strict and paranoid ModemManager filter-policy 2019-03-22 20:00:02 +01:00
8f6ae29163 Fix ModemManager udev rule for some distros 2019-03-22 20:00:02 +01:00
a0d27c2c56 add memory layout commment, undo -8 simplification 2019-03-22 19:55:25 +01:00
3a10427bd9 remove unused files 2019-03-22 19:55:25 +01:00
f3b591e570 Apply suggestions from code review
Co-Authored-By: conorpp <conorpp94@gmail.com>
2019-03-22 19:55:25 +01:00
175f59d206 paste into other linker scripts 2019-03-22 19:55:25 +01:00
f5ff6a11f0 rewrite base linker script 2019-03-22 19:55:25 +01:00
d979420324 u2f work with yubikey5 2019-03-22 01:59:19 -04:00
5076af1be4 works with yubikey5 2019-03-22 01:22:55 -04:00
0a7845459c breakup test_fido2 2019-03-22 00:45:28 -04:00
c4262b0f5b rename 2019-03-22 00:20:48 -04:00
53fb0059a7 break into separate files 2019-03-22 00:20:20 -04:00
a1a75e4ab5 check errors 2019-03-21 12:47:15 -04:00
d68011ef04 remove warnings 2019-03-21 00:01:37 -04:00
02e83073e0 add hmac-secret to reg response 2019-03-20 23:58:42 -04:00
3a48756f96 remove extra layer of map 2019-03-20 23:40:58 -04:00
946e932b1e refactor to use less ram 2019-03-20 23:28:45 -04:00
142d4002e5 remove warning, reduce memory 2019-03-20 23:14:17 -04:00
dbe5283e1f test solo commands on fido2 layer 2019-03-20 21:06:18 -04:00
2d233f164e small bug fixes 2019-03-20 21:03:03 -04:00
b62e9906c7 make new function 2019-03-20 20:13:16 -04:00
e22e636475 hmac-secret tested 2019-03-20 20:03:25 -04:00
074225d87a hmac-secret fully functional 2019-03-20 20:03:12 -04:00
bb9b2ea9d4 validate saltAuth 2019-03-20 18:10:52 -04:00
e8d5bc5829 refactor ctap_make_auth_data arguments 2019-03-20 17:43:50 -04:00
850381a633 test parsing 2019-03-20 16:52:10 -04:00
ce3ad0e56f bugfix 2019-03-20 16:51:58 -04:00
00d86379e5 parse full hmac-secret 2019-03-20 16:21:21 -04:00
6098810167 start to test hmac-secret 2019-03-20 15:45:35 -04:00
821880a8d6 parse extension info in MC 2019-03-20 15:45:10 -04:00
44f96f5843 Merge pull request #148 from solokeys/testing_refactor
Testing refactor
2019-03-20 15:06:12 -04:00
6ec9fb962a delay send_raw in test 2019-03-20 14:59:31 -04:00
c9bfe001ee refactored version, previously lost 2019-03-20 14:56:52 -04:00
5e46fd96ac Create LICENSE 2019-03-19 16:29:39 +01:00
103cc3cfb0 Some fun with shields 2019-03-19 16:28:47 +01:00
9544330dc3 delay send_raw in test 2019-03-18 04:36:02 +01:00
0964ff69b7 refactor a bit 2019-03-18 04:36:02 +01:00
e4a2b9e1ca Get udev instructions up to date 2019-03-18 02:52:34 +01:00
d29fa34da1 Update README.md 2019-03-15 22:08:07 -04:00
6ed2610a5c Merge pull request #145 from solokeys/stable-v2.0.0
Set STABLE_VERSION to 2.0.0
2019-03-14 19:39:43 -04:00
3b9d4e5023 Set STABLE_VERSION to 2.0.0 2019-03-15 00:35:50 +01:00
2da083c18a Merge pull request #138 from solokeys/fix-warning
I think this does it
2019-03-11 14:33:02 -04:00
50bfbc1eff I think this does it 2019-03-10 15:26:45 +01:00
86739df7a1 Improvements to Docker build 2019-03-10 15:19:16 +01:00
c7f0d050d7 Fix udev rule for STM bootloader 2019-03-08 14:25:29 +01:00
b79670a447 Merge pull request #133 from solokeys/more_testing
More testing
2019-03-07 21:25:53 -05:00
169ba59ed4 use alpha 2019-03-07 21:19:10 -05:00
f3003c58c9 new release 2.0.0 2019-03-07 20:33:20 -05:00
084e518018 refactor 2019-03-06 17:43:28 -05:00
6674f0a8ff add more tests 2019-03-06 16:17:12 -05:00
f704851419 add bootloader tests 2019-03-06 14:23:56 -05:00
0d5e1ee872 Test solo specific commands 2019-03-06 14:06:07 -05:00
5cb81c753d Add version/extensions to PC build 2019-03-06 14:05:44 -05:00
b0b0564df9 fix imports 2019-03-06 13:13:43 -05:00
195dc2a8ae use 0x7f as upper counter byte 2019-03-04 02:36:47 -05:00
4982b13f64 Merge pull request #129 from solokeys/testing_fido2
Testing fido2
2019-03-04 02:26:06 -05:00
63a93f6ec2 test pin lock out 2019-03-03 19:01:08 -05:00
7b8ec18e76 add reboot capability for tests 2019-03-03 18:43:14 -05:00
67faef0117 tests for client pin 2019-03-03 17:17:04 -05:00
0b9f0af3c7 spin pc less 2019-03-03 17:15:26 -05:00
880d54a4f0 more fido2 tests 2019-03-03 03:43:15 -05:00
1507758ad1 bring pc crypto impl up to date 2019-03-02 23:10:43 -05:00
e883c5aa6e add many fido2 tests 2019-03-02 22:40:51 -05:00
afc85e0d2e update log message 2019-03-02 22:40:27 -05:00
a40dcf3f17 reduce nfc detect period to 10ms 2019-03-02 20:08:28 -05:00
4b82e80d7a init device with nfc detection 2019-03-02 20:08:15 -05:00
246dea8a44 fix clock init by setting flash latency last for low freqs 2019-03-02 20:05:27 -05:00
7a98764a5b organize ECC flags 2019-03-02 19:48:09 -05:00
dc946f5b35 centralize reset key agreement 2019-03-02 19:38:27 -05:00
0232893611 increase buffer size for USB strings, check string length 2019-03-02 19:38:03 -05:00
5995f84822 buffering logs sometimes freezes, stop for now 2019-03-02 15:43:12 -05:00
1ff00895b1 init nfc, fix freeze 2019-03-02 02:02:01 -05:00
83641b3789 disable clock settings for NFC passive for now 2019-03-02 01:30:09 -05:00
9b356076c5 Merge pull request #127 from solokeys/testing
Testing
2019-03-02 01:12:48 -05:00
6c96521c7d slight cleanup 2019-03-02 00:55:46 -05:00
35707c3797 wrong size, causes RK's to be overwritten 2019-03-02 00:55:25 -05:00
e31e703afd minor improvements 2019-03-01 23:42:22 -05:00
3a8be9eef7 add more u2f tests 2019-03-01 23:16:48 -05:00
e2b30ec087 basic interface 2019-03-01 22:35:50 -05:00
495e10f3a1 add basic rk support for pc 2019-03-01 22:28:25 -05:00
11ca6bd517 fix pc testing 2019-03-01 22:11:36 -05:00
a265da09fb Update u2f.c 2019-03-01 22:00:17 -05:00
1b4d1be9ee Merge pull request #119 from solokeys/udev
Cleanup udev rules, keep 99-solo.rules as symlink
2019-03-01 21:44:45 -05:00
32f2436380 Merge pull request #120 from nickray/sha512
SHA512 (via Cifra)
2019-03-01 21:44:13 -05:00
7255c4f8db Merge pull request #121 from solokeys/nfc
Nfc
2019-03-01 21:43:12 -05:00
4e215db42a start from 0 2019-02-28 23:13:12 -05:00
a1ad641076 Merge branch 'master' of github.com:solokeys/solo 2019-02-28 22:47:25 -05:00
daf56b0cc7 Silence warning about out of date pip in Travis 2019-02-28 01:06:06 +01:00
5859073cb8 Build bundle-hacker-{version}.hex 2019-02-28 01:06:06 +01:00
ff5207ba77 First attempt 2019-02-27 21:43:20 +01:00
324b4a89cc Remove python-fido2 submodule 2019-02-27 21:43:20 +01:00
9f60caf9c1 for docker on windows 2019-02-26 22:00:21 -05:00
0865f2a660 do not probe bootloader 2019-02-27 03:18:12 +01:00
b1c72c9d94 for docker on windows 2019-02-26 21:11:33 -05:00
5e70c11b54 Hide onboard crypto tests behind a reserved ctaphid command 2019-02-27 02:58:56 +01:00
46ada5a8b9 WRONG_DATA apdu error code fix 2019-02-26 20:34:07 -05:00
0eac67259d remove stm32l442 target 2019-02-26 18:24:40 -05:00
9399a5f195 Cleanup udev rules, keep 99-solo.rules as symlink 2019-02-27 00:00:49 +01:00
47aa287480 Vanilla cifra needs more includes 2019-02-26 23:50:01 +01:00
865b698bed ...and bootloader 2019-02-26 23:33:57 +01:00
14974e0ebe fix compile issues 2019-02-26 15:30:57 -05:00
1ed7833c9f fix pc build 2019-02-26 15:08:09 -05:00
e8d0ad5e7c autodetect passive nfc operation or usb operation 2019-02-26 15:04:23 -05:00
e2ca7f52db optimize ecc for arm 2019-02-26 14:19:07 -05:00
c97b9f9b8f Need includes in main Makefile too 2019-02-26 20:16:38 +01:00
ecf994b647 fix warnings and compile errors 2019-02-26 14:13:29 -05:00
347d0942b1 refactor fromNFC 2019-02-26 14:07:27 -05:00
ff0d42c8d5 refactor clock rates, fix warnings 2019-02-26 13:56:06 -05:00
a6673b0917 Use our cifra fork, rename command, keep room for sha256 2019-02-26 19:52:59 +01:00
0c296bba30 First go at using cifra for SHA512 2019-02-26 19:52:59 +01:00
57930aaa13 fix compilation errors 2019-02-26 13:27:25 -05:00
1a6895ca25 merge 2019-02-26 13:10:16 -05:00
54241ecd42 add option 'sim' to select UDP/simulated backend 2019-02-26 18:37:42 +01:00
e537d00173 update to new fido2 version 2019-02-26 18:37:42 +01:00
a195408a11 scale up to 24 MHz only for register 2019-02-26 01:51:07 -05:00
54b7f42056 passive operation works as is (refactor needed) 2019-02-26 01:19:35 -05:00
6128e86da2 Merge pull request #114 from merlokk/nfc4
NFC. added hardware part of UID
2019-02-19 22:05:15 -05:00
fed9f473aa added hardware part of UID 2019-02-19 18:12:01 +02:00
f6ff3c1b87 Fetch tags in docker build script. More robust udev rules in docs 2019-02-19 00:32:07 +01:00
afd3218358 Create CHANGELOG.md 2019-02-17 18:19:30 -05:00
ed6da0ba1e Merge pull request #111 from solokeys/fido2-ext
Fido2 ext
2019-02-17 15:51:57 -05:00
46d7be865d fix upper byte U2F for backwards compatibility 2019-02-17 15:33:24 -05:00
596c6c1077 manually specify order for reproducible builds 2019-02-16 22:30:49 -05:00
6c3014575f Update application.mk 2019-02-16 22:02:26 -05:00
190ecc8fd8 make work for windows 2019-02-16 21:27:56 -05:00
0d2e03a5a9 Change firmware-hacker ROP level, add ST DFU udev
- later we can set ROP=1 for hacker firmware builds again,
  right now it causes issues in solo-python tool
2019-02-17 02:30:16 +01:00
991530f88b generate the serial number same as DFU 2019-02-16 14:08:59 -05:00
de31924be3 Lock down reproducible make targets and use in docker build 2019-02-16 18:46:13 +01:00
6b97807f51 Easier hex make targets for docker build 2019-02-16 18:36:04 +01:00
35022775cd Ensure the docker image has all public commits to build from 2019-02-16 18:10:16 +01:00
6fecb3c035 base serial number off of chip Unique device ID 2019-02-16 00:23:11 -05:00
3fed8cebdf reduce RNG to 71 2019-02-14 18:01:23 -05:00
c81bc9fb98 Detailed version in product name 2019-02-14 23:13:06 +01:00
99f09790f1 deterministic 2019-02-14 16:03:19 -05:00
6745c9a0cb bugfix/skip-auth for fido2 extension 2019-02-14 15:53:02 -05:00
0651316da5 catch U2F check by extension 2019-02-14 15:16:13 -05:00
f48becc6dc bridge extension to fido2 interface 2019-02-14 15:15:58 -05:00
85c58e9d5b TAG_EXT typo 2019-02-14 15:15:24 -05:00
c9862977bf delete old key 2019-02-13 19:22:45 -05:00
1a40299dcb add solokeys cert 2019-02-13 19:16:44 -05:00
8f9ff17bef ability to build solo versions via make docker-build SOLO_VERSION=... 2019-02-14 00:35:28 +01:00
9e9d26e604 Split building and merging firmware in two, use volumes 2019-02-14 00:35:28 +01:00
b3d76d56e0 Add docker-build make target, adjust instructions, remove Python2 support 2019-02-14 00:35:28 +01:00
13424fdbcd add Dockerfile 2019-02-14 00:35:28 +01:00
3b320e0aeb initialize at 24 MHz at very start instead of 16 2019-02-12 18:34:13 -05:00
529b879c08 init in device_init 2019-02-11 22:02:41 -05:00
2893cd7ce3 move inits to device_init 2019-02-11 22:00:18 -05:00
120fb95541 compile for bootloader 2019-02-08 13:09:32 -05:00
665e84d183 delay to allow spi interface to initialize 2019-02-08 13:09:12 -05:00
13d9885da4 initialize at 16MHz, add 24 and 32 options 2019-02-08 13:08:28 -05:00
e230a9464e enable ctap from usb 2019-02-07 20:09:13 -05:00
342af18b1f initialize ctap 2019-02-07 20:09:00 -05:00
be9bd941c8 simplify ams init 2019-02-07 19:45:02 -05:00
0f6be6740b Merge pull request #101 from merlokk/nfc2
Nfc2
2019-02-07 19:14:12 -05:00
9054736e0e delete debugging code 2019-02-07 19:03:45 -05:00
c6d946136e small fix 2019-02-07 16:05:08 +02:00
32400c8d09 Merge branch 'nfc' into nfc2 2019-02-07 14:49:47 +02:00
587c9aad14 refactor 2019-02-06 18:09:53 -05:00
c624a32ef6 default 8 thread build 2019-02-06 18:07:38 -05:00
3005a63938 re-arrange some logic for better passive operation 2019-02-06 18:07:09 -05:00
f470e9a9cd dont need to init clock at first in bootloader 2019-02-06 18:05:22 -05:00
e3971a5e0f change ams init, read less regs 2019-02-06 17:56:53 -05:00
2ed8667f18 immedately change clock rate to load data sections faster 2019-02-06 17:55:27 -05:00
765d532f82 add low freq clocking options 4,8,16MHz 2019-02-06 17:54:52 -05:00
ca05385513 log fixing 2019-02-06 20:06:46 +02:00
5328610ff1 delete debug messages 2019-02-06 19:51:32 +02:00
bc1bb3509f move APDU dumps to separate log channel 2019-02-06 19:21:06 +02:00
375db69e3a fido2 works 2019-02-06 19:06:49 +02:00
771fffe329 WTX works. todo: clean debug unneeded messages 2019-02-06 17:12:22 +02:00
4611f05051 small fix in AMS3956 debug texts 2019-02-06 15:17:52 +02:00
e657e26886 check AMS product type 2019-02-06 15:15:37 +02:00
3ffcc47374 fix logger 2019-02-06 14:11:49 +02:00
1b5e230d45 merge u2f endian fix 2019-02-02 00:32:36 -05:00
81a89ed6aa go back to high freq 2019-02-02 00:29:32 -05:00
ca2074de36 Update Makefile 2019-02-02 00:25:01 -05:00
ee98340a03 temporarily remove prints at start 2019-02-02 00:24:42 -05:00
3d0d91fa5c lf param 2019-02-02 00:24:11 -05:00
38171dba06 low freq init 2019-02-02 00:23:51 -05:00
4ba57ccc85 refactor init functions 2019-02-02 00:23:01 -05:00
c3bddee814 dont do this when powered by nfc 2019-02-02 00:21:26 -05:00
b7bc50bc4f Merge pull request #96 from merlokk/nfc2
small fixes in NFC branch
2019-02-02 00:16:07 -05:00
19627a959a some TODOs 2019-02-01 21:35:45 +02:00
429e4b2a77 add WTX_clear(); 2019-02-01 21:33:57 +02:00
6e5de7bd6b read data if we sent WTX 2019-02-01 21:31:20 +02:00
c6daa4acc9 more WTX sketch 2019-02-01 21:27:43 +02:00
ab01d0c73d delete comment 2019-02-01 21:02:02 +02:00
0ef42b2df7 added WTX sending sketch 2019-02-01 20:45:36 +02:00
f6e2bfa683 yubikey answers U2F_SW_WRONG_PAYLOAD instead of U2F_SW_WRONG_DATA 2019-02-01 20:06:19 +02:00
5c8acdd666 fix u2f user presence check, added dont-enforce-user-presence-and-sign, fix counter 2019-02-01 20:00:13 +02:00
e996d470f9 small fixes 2019-02-01 19:15:48 +02:00
e2e29492e6 point to website 2019-01-29 22:39:49 -05:00
5f637992b1 implement capability container and ndef tag to work with nexus 6 2019-01-29 22:12:38 -05:00
91d092a27a tell AMS to go to sleep if deselected 2019-01-27 23:55:11 -05:00
23cbfde312 Merge pull request #88 from merlokk/nfc
merging so I can test out in my branch
2019-01-27 21:11:57 -05:00
cce25b2a1c u2f auth works 2019-01-28 00:04:17 +02:00
f24058d2e8 u2f authenticate wrong length fix 2019-01-27 23:58:35 +02:00
4c941997b4 check as3956 on startup 2019-01-27 23:35:20 +02:00
2049020b92 refactoring 2019-01-27 11:44:33 +02:00
1857482617 add some len check 2019-01-27 00:01:04 +02:00
2feef8b043 add some profiling... 2019-01-26 23:53:13 +02:00
3eddfbf8a9 u2f register works 2019-01-26 23:44:51 +02:00
a662a9a619 remove dump 2019-01-26 23:36:45 +02:00
1a656d60e4 register works. but it needs to press a button.... 2019-01-26 23:35:45 +02:00
e235402fb8 u2f register 2019-01-26 21:34:53 +02:00
6ca9f1946b I block on receive 2019-01-26 21:08:18 +02:00
df671775ba add some profiling. looks good. 2019-01-26 19:30:03 +02:00
3ba83f6407 remove debug msg 2019-01-26 19:11:51 +02:00
ffa4225827 chip have too less memory. so reusing ctap_resp buffer. 2019-01-26 19:07:12 +02:00
cde6bc107a GetVersion works. not so clean. needs additional memory.... 2019-01-25 19:54:02 +02:00
15de8dc4a6 send response from key to pc in chaining mode. partially works.
GetVersion must work with pc (proxmark have errors)
2019-01-25 19:32:42 +02:00
94fe58d020 small fix 2019-01-24 20:09:57 +02:00
e8634a2d61 add u2f errors 2019-01-24 20:04:44 +02:00
67b0abde4b some refactoring 2019-01-24 19:56:18 +02:00
d713167ec4 works. needs to add chaining 2019-01-24 19:21:31 +02:00
45888c9a25 ins check is ok 2019-01-24 18:22:58 +02:00
d02206ba09 SELECT works as expected and U2F GetVersion command done 2019-01-24 18:19:23 +02:00
ad9186c13b SELECT works 2019-01-24 17:57:42 +02:00
4e0dc15dfd add historical bytes 2019-01-24 16:56:38 +02:00
dcf7940b3d basic ndef message works 2019-01-13 17:25:32 -05:00
1874e11fba organize 2019-01-13 00:02:37 -05:00
302ce75ce6 responds to RATS correctly 2019-01-12 20:20:47 -05:00
62cd7cc728 enable energy harvesting and tunneling in eeprom 2019-01-12 16:20:11 -05:00
20f8aac768 option to boot at 4MHz with no USB 2019-01-12 16:19:44 -05:00
121070822f Update main.c 2019-01-07 21:20:07 -05:00
96f65be9c2 disable main app for now 2019-01-07 21:19:56 -05:00
78c40976c3 log interrupts and recv'd data 2019-01-07 21:19:45 -05:00
aa978abfc7 cleanup 2019-01-07 19:40:20 -05:00
b7c0e4ea92 no delays 2019-01-07 19:05:39 -05:00
6ffba7d472 move to new file 2019-01-07 18:50:01 -05:00
c330346c31 add nfc log tag 2019-01-07 18:29:38 -05:00
eda26e3c93 add ST spi LL driver 2019-01-07 18:17:33 -05:00
44077a4f2f spi interface WORKS 2019-01-06 17:12:26 -05:00
4c6f0969c1 add spi 2019-01-05 20:58:39 -05:00
87 changed files with 7519 additions and 3442 deletions

12
.gitignore vendored
View File

@ -81,13 +81,5 @@ env3/
.tags*
targets/*/docs/
main
targets/efm32/.project
targets/efm32/.settings/com.silabs.ss.framework.ide.project.sls.core.prefs
targets/efm32/.settings/org.eclipse.cdt.codan.core.prefs
targets/efm32/CMSIS/EFM32PG1B/startup_gcc_efm32pg1b.s
targets/efm32/CMSIS/EFM32PG1B/system_efm32pg1b.c
targets/efm32/EFM32.hwconf
targets/efm32/EFM32_EFM32JG1B200F128GM32.hwconf
targets/efm32/emlib/em_adc.c
targets/efm32/emlib/em_assert.c
targets/efm32/emlib/em_cmu.c
builds/*

6
.gitmodules vendored
View File

@ -1,9 +1,6 @@
[submodule "tinycbor"]
path = tinycbor
url = https://github.com/intel/tinycbor
[submodule "python-fido2"]
path = python-fido2
url = https://github.com/solokeys/python-fido2
[submodule "crypto/micro-ecc"]
path = crypto/micro-ecc
url = https://github.com/kmackay/micro-ecc.git
@ -13,3 +10,6 @@
[submodule "targets/stm32l442/dfuse-tool"]
path = targets/stm32l442/dfuse-tool
url = https://github.com/solokeys/dfuse-tool
[submodule "crypto/cifra"]
path = crypto/cifra
url = https://github.com/solokeys/cifra.git

View File

@ -1,25 +0,0 @@
# Notify ModemManager this device should be ignored
ACTION!="add|change|move", GOTO="mm_usb_device_blacklist_end"
SUBSYSTEM!="usb", GOTO="mm_usb_device_blacklist_end"
ENV{DEVTYPE}!="usb_device", GOTO="mm_usb_device_blacklist_end"
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ENV{ID_MM_DEVICE_IGNORE}="1"
LABEL="mm_usb_device_blacklist_end"
# Solo
## access
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", GROUP="plugdev"
## Solo Secure symlink
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ATTRS{product}=="Solo [1-9]*", SYMLINK+="solokey"
## Solo Hacker symlink
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ATTRS{product}=="Solo Hacker [1-9]*", SYMLINK+="solohacker"
## Solo Serial access + symlink
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", GROUP="plugdev", SYMLINK+="soloserial"
# U2F Zero
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess", GROUP="plugdev", SYMLINK+="u2fzero"

1
99-solo.rules Symbolic link
View File

@ -0,0 +1 @@
udev/70-solokeys-access.rules

1
ALPHA_VERSION Normal file
View File

@ -0,0 +1 @@
2.0.0

27
CHANGELOG.md Normal file
View File

@ -0,0 +1,27 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [1.1.0] - 2019-02-17
### Added
- Code cleanup
- Buffer over-read bug fix
- U2F counter endianness bug fix
- More testing
- Extension interface to U2F and FIDO2
- Read firmware version
- Read RNG bytes
## [1.1.1] - 2019-03-01
- This version fixes an incorrect error code returned in U2F.
## [2.0.0] - 2019-03-01
- Merge of NFC functionality branch
- Bug fix with compiled USB name being too long causing buffer overrun
- Change upper byte of counter from `0xff` to `0x7f` to fix issues with some websites.

33
Dockerfile Normal file
View File

@ -0,0 +1,33 @@
FROM debian:stretch-slim
MAINTAINER SoloKeys <hello@solokeys.com>
RUN apt-get update -qq
RUN apt-get install -qq bzip2 git make wget >/dev/null
# 1. ARM GCC: for compilation
RUN wget -q -O gcc.tar.bz2 https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2018q4/gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2?revision=d830f9dd-cd4f-406d-8672-cca9210dd220?product=GNU%20Arm%20Embedded%20Toolchain,64-bit,,Linux,8-2018-q4-major
# from website
RUN echo "f55f90d483ddb3bcf4dae5882c2094cd gcc.tar.bz2" > gcc.md5
RUN md5sum -c gcc.md5
# self-generated
RUN echo "fb31fbdfe08406ece43eef5df623c0b2deb8b53e405e2c878300f7a1f303ee52 gcc.tar.bz2" > gcc.sha256
RUN sha256sum -c gcc.sha256
RUN tar -C /opt -xf gcc.tar.bz2
# 2. Python3.7: for solo-python (merging etc.)
RUN wget -q -O miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-4.5.12-Linux-x86_64.sh
# from website
RUN echo "866ae9dff53ad0874e1d1a60b1ad1ef8 miniconda.sh" > miniconda.md5
RUN md5sum -c miniconda.md5
# self-generated
RUN echo "e5e5b4cd2a918e0e96b395534222773f7241dc59d776db1b9f7fedfcb489157a miniconda.sh" > miniconda.sha256
RUN sha256sum -c miniconda.sha256
RUN bash ./miniconda.sh -b -p /opt/conda
RUN ln -s /opt/conda/bin/python /usr/local/bin/python3
RUN ln -s /opt/conda/bin/python /usr/local/bin/python
RUN ln -s /opt/conda/bin/pip /usr/local/bin/pip3
RUN ln -s /opt/conda/bin/pip /usr/local/bin/pip
# 3. Source code
RUN git clone --recurse-submodules https://github.com/solokeys/solo /solo --config core.autocrlf=input

1
LICENSE Normal file
View File

@ -0,0 +1 @@
Apache-2.0 OR MIT

View File

@ -9,7 +9,9 @@
ecc_platform=2
src = $(wildcard pc/*.c) $(wildcard fido2/*.c) $(wildcard crypto/sha256/*.c) crypto/tiny-AES-c/aes.c
src = $(wildcard pc/*.c) $(wildcard fido2/*.c) $(wildcard fido2/extensions/*.c) \
$(wildcard crypto/sha256/*.c) crypto/tiny-AES-c/aes.c
obj = $(src:.c=.o) crypto/micro-ecc/uECC.o
LIBCBOR = tinycbor/lib/libtinycbor.a
@ -20,9 +22,20 @@ else
export LDFLAGS = -Wl,--gc-sections
endif
LDFLAGS += $(LIBCBOR)
CFLAGS = -O2 -fdata-sections -ffunction-sections
VERSION:=$(shell git describe --abbrev=0 )
VERSION_FULL:=$(shell git describe)
VERSION_MAJ:=$(shell python -c 'print("$(VERSION)".split(".")[0])')
VERSION_MIN:=$(shell python -c 'print("$(VERSION)".split(".")[1])')
VERSION_PAT:=$(shell python -c 'print("$(VERSION)".split(".")[2])')
VERSION_FLAGS= -DSOLO_VERSION_MAJ=$(VERSION_MAJ) -DSOLO_VERSION_MIN=$(VERSION_MIN) \
-DSOLO_VERSION_PATCH=$(VERSION_PAT) -DSOLO_VERSION=\"$(VERSION_FULL)\"
CFLAGS = -O2 -fdata-sections -ffunction-sections $(VERSION_FLAGS)
INCLUDES = -I./tinycbor/src -I./crypto/sha256 -I./crypto/micro-ecc/ -Icrypto/tiny-AES-c/ -I./fido2/ -I./pc -I./fido2/extensions
INCLUDES += -I./crypto/cifra/src
CFLAGS += $(INCLUDES)
# for crypto/tiny-AES-c
@ -30,7 +43,7 @@ CFLAGS += -DAES256=1 -DAPP_CONFIG=\"app.h\"
name = main
.PHONY: all $(LIBCBOR) black blackcheck cppcheck wink fido2-test clean full-clean travis test clean
.PHONY: all $(LIBCBOR) black blackcheck cppcheck wink fido2-test clean full-clean travis test clean version
all: main
tinycbor/Makefile crypto/tiny-AES-c/aes.c:
@ -42,6 +55,9 @@ cbor: $(LIBCBOR)
$(LIBCBOR):
cd tinycbor/ && $(MAKE) clean && $(MAKE) -j8
version:
@git describe
test: venv
$(MAKE) clean
$(MAKE) -C . main
@ -58,6 +74,7 @@ crypto/micro-ecc/uECC.o: ./crypto/micro-ecc/uECC.c
venv:
python3 -m venv venv
venv/bin/pip -q install --upgrade pip
venv/bin/pip -q install --upgrade -r tools/requirements.txt
venv/bin/pip -q install --upgrade black
@ -66,11 +83,24 @@ black: venv
venv/bin/black --skip-string-normalization --check tools/
wink: venv
venv/bin/python tools/solotool.py solo --wink
venv/bin/solo key wink
fido2-test: venv
venv/bin/python tools/ctap_test.py
DOCKER_IMAGE := "solokeys/solo-firmware:local"
SOLO_VERSIONISH := "master"
docker-build:
docker build -t $(DOCKER_IMAGE) .
docker run --rm -v "$(CURDIR)/builds:/builds" \
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
$(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
uncached-docker-build:
docker build --no-cache -t $(DOCKER_IMAGE) .
docker run --rm -v "$(CURDIR)/builds:/builds" \
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
$(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
CPPCHECK_FLAGS=--quiet --error-exitcode=2
cppcheck:

View File

@ -4,6 +4,12 @@
[![Keybase Chat](https://img.shields.io/badge/chat-on%20keybase-brightgreen.svg)](https://keybase.io/team/solokeys.public)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo?ref=badge_shield)
[![latest release](https://img.shields.io/github/release/solokeys/solo.svg)](https://github.com/solokeys/solo/releases)
[![commits since last release](https://img.shields.io/github/commits-since/solokeys/solo/latest.svg)](https://github.com/solokeys/solo/commits/master)
[![last commit](https://img.shields.io/github/last-commit/solokeys/solo.svg)](https://github.com/solokeys/solo/commits/master)
[![commit activity](https://img.shields.io/github/commit-activity/m/solokeys/solo.svg)](https://github.com/solokeys/solo/commits/master)
[![contributors](https://img.shields.io/github/contributors/solokeys/solo.svg)](https://github.com/solokeys/solo/graphs/contributors)
# Solo
@ -13,7 +19,7 @@ Solo supports FIDO2 and U2F standards for strong two-factor authentication and p
<img src="https://solokeys.com/images/photos/hero-on-white-cropped.png" width="600">
This repo contains the Solo firmware, including implementations of FIDO2 and U2F (CTAP2 and CTAP) over USB and NFC. The main implementation is for STM32L432, and it's ported to NRF52840 and EFM32J.
This repo contains the Solo firmware, including implementations of FIDO2 and U2F (CTAP2 and CTAP) over USB and NFC. The main implementation is for STM32L432, but it is easily portable.
For development no hardware is needed, Solo also runs as a standalone application for Windows, Linux, and Mac OSX. If you like (or want to learn) hardware instead, you can run Solo on the NUCLEO-L432KC development board, or we make Solo for Hacker, an unlocked version of Solo that lets you customize its firmware.
@ -33,9 +39,9 @@ Solo is based on the STM32L432 microcontroller. It offers the following security
Solo for Hacker is a special version of Solo that let you customize its firmware, for example you can change the LED color, and even build advanced applications.
You can only buy Solo for Hacker at [solokeys.com](https://solokeys.com), as we don't sell it on Amazon and other places to avoid confusing customers. If you buy a Hacker, you can permanently lock it into a regular Solo, but viceversa you can NOT take a regular Solo and turn it a Hacker.
Check out [solokeys.com](https://solokeys.com), for options on where to buy Solo. Solo Hacker can be converted to a secure version, but normal Solo cannot be converted to a Hacker version.
If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/).
If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/). We only support Python3.
```bash
git clone --recurse-submodules https://github.com/solokeys/solo
@ -43,27 +49,27 @@ cd solo
cd targets/stm32l432
make cbor
make all-hacker
make build-hacker
cd ../..
make venv
source venv/bin/activate
python tools/solotool.py program targets/stm32l432/solo.hex
solo program aux enter-bootloader
solo program bootloader targets/stm32l432/solo.hex
```
Alternatively, run `make docker-build` and use the firmware generated in `/tmp`.
If you forgot the `--recurse-submodules` when cloning, simply `git submodule update --init --recursive`.
For example, if you want to turn off any blue light emission, you can edit [`led_rgb()`](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/led.c#L15) and force:
```
uint32_t b = 0;
```
For example, if you want to turn off any blue light emission, you can edit [`led_rgb()`](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/app.h#L48) and change `LED_INIT_VALUE`
to be a different hex color.
Then recompile, load your new firmware, and enjoy a blue-light-free version of Solo.
Then recompile, load your new firmware, and enjoy a different LED color Solo.
In the Hacker version, hardware is the same and firmware is unlocked, in the sense that you can 1) load an unsigned application, or 2) entirely reflash the key. By contrast, in a regular Solo you can only upgrade to a firmware signed by SoloKeys, and flash is locked and debug disabled permanently.
A frequently asked question is whether Solo for Hacker is less secure than regular Solo. The answer is certainly yes, and therefore we only recommend to use Solo for Hacker for development, experimentation, and fun. An attacker with physical access to a Solo for Hacker can reflash it following the steps above, and even a malware on your computer could possibly reflash it.
In the Hacker version, hardware is the same but the firmware is unlocked, so you can 1) load an unsigned application, or 2) entirely reflash the key. By contrast, in a regular Solo you can only upgrade to a firmware signed by SoloKeys, and flash is locked and debug disabled permanently.
Hacker Solo isn't really secure so you should only use it for development. An attacker with physical access to a Solo for Hacker can reflash it following the steps above, and even a malware on your computer could possibly reflash it.
# Developing Solo (No Hardware Needed)
@ -80,7 +86,7 @@ This builds Solo as a standalone application. Solo application is set up to send
Testing can be done using our fork of Yubico's client software, python-fido2. Our fork of python-fido2 has small changes to make it send USB HID over UDP to the authenticator application. You can install our fork by running the following:
```bash
cd python-fido2 && python setup.py install
pip install -r tools/requirements.txt
```
Run the Solo application:
@ -90,12 +96,7 @@ Run the Solo application:
In another shell, you can run client software, for example our tests:
```bash
python tools/ctap_test.py
```
Or any client example such as:
```bash
python python-fido2/examples/credential.py
python tools/ctap_test.py sim fido2
```
You can find more details in our [documentation](https://docs.solokeys.io/solo/), including how to build on the the NUCLEO-L432KC development board.

1
STABLE_VERSION Normal file
View File

@ -0,0 +1 @@
2.0.0

0
builds/.gitkeep Normal file
View File

1
crypto/cifra Submodule

Submodule crypto/cifra added at d04dd31860

View File

@ -55,11 +55,11 @@ If you use `DEBUG=2`, that means Solo will not boot until something starts readi
it's debug messages. So it basically it waits to tether to a serial terminal so that you don't
miss any debug messages.
We recommend using our `solotool.py` as a serial emulator since it will automatically
We recommend using our `solo` tool as a serial emulator since it will automatically
reconnect each time you program Solo.
```
python tools/solotool.py monitor <serial-port>
solo monitor <serial-port>
```
#### Linux Users:
@ -86,7 +86,7 @@ Programming `all.hex` will cause the device to permanently lock itself.
It's recommended to test a debug/hacker build first to make sure Solo is working as expected.
Then you can switch to a locked down build, which cannot be reprogrammed as easily (or not at all!).
We recommend using our `solotool.py` to manage programming. It is cross platform. First you must
We recommend using our `solo` tool to manage programming. It is cross platform. First you must
install the prerequisites:
```
@ -101,7 +101,8 @@ If your Solo device is already programmed (it flashes green when powered), we re
programming it using the Solo bootloader.
```
python tools/solotool.py program solo.hex
solo program aux enter-bootloader
solo program bootloader solo.hex
```
Make sure to program `solo.hex` and not `all.hex`. Nothing bad would happen, but you'd
@ -125,7 +126,10 @@ off and it enumerates as "STM BOOTLOADER".
You can program it by running the following.
```
python tools/solotool.py program all.hex --use-dfu --detach
solo program aux enter-bootloader
solo program aux enter-dfu
# powercycle key
solo program dfu all.hex
```
Make sure to program `all.hex`, as this contains both the bootloader and the Solo application.
@ -145,14 +149,14 @@ A locked Solo will only accept signed updates.
If this is not a device with a hacker build, you can only program signed updates.
```
python tools/solotool.py program /path/to/firmware.json
solo program bootloader /path/to/firmware.json
```
If you've provisioned the Solo bootloader with your own secp256r1 public key, you can sign your
firmware by running the following command.
```
python tools/solotool.py sign /path/to/signing-key.pem /path/to/solo.hex /output-path/to/firmware.json
solo sign /path/to/signing-key.pem /path/to/solo.hex /output-path/to/firmware.json
```
If your Solo isn't locked, you can always reprogram it using a debugger connected directly
@ -175,5 +179,5 @@ If you'd like to also permanently disable signed updates, plug in your programme
```
# WARNING: No more signed updates.
python tools/programmer.py --disable
solo program disable-bootloader
```

View File

@ -1,21 +1,31 @@
# tl;dr
# Summary
Create [`/etc/udev/rules.d/99-solo.rules`](https://github.com/solokeys/solo/blob/master/99-solo.rules) and add the following (which assumes your user is in group `plugdev`):
On Linux, by default USB dongles can't be accessed by users, for security reasons. To allow user access, so-called "udev rules" must be installed. (Under Fedora, your key may work without such a rule.)
Create a file like [`70-solokeys-access.rules`](https://github.com/solokeys/solo/blob/master/udev/70-solokeys-access.rules) in your `/etc/udev/rules.d` directory, for instance the following rule should cover normal access (it has to be on one line):
```
# Solo
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", GROUP="plugdev", SYMLINK+="solokey"
# U2F Zero
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess", GROUP="plugdev", SYMLINK+="u2fzero"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", MODE="0660", GROUP="plugdev"
```
Then run
Additionally, run the following command after you create this file (it is not necessary to do this again in the future):
```
sudo udevadm control --reload-rules && sudo udevadm trigger
```
A simple way to setup both the udev rule and the udevadm reload is:
```
git clone git@github.com:solokeys/solo.git
cd solo/udev
make setup
```
We are working on getting user access to Solo keys enabled automatically in common Linux distributions: <https://github.com/solokeys/solo/issues/144>.
# How do udev rules work and why are they needed
In Linux, `udev` (part of `systemd`, read `man 7 udev`) handles "hot-pluggable" devices, of which Solo and U2F Zero are examples. In particular, it creates nodes in the `/dev` filesystem (in Linux, everything is a file), which allow accessing the device.

30
fido2/apdu.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef _APDU_H_
#define _APDU_H_
#include <stdint.h>
typedef struct
{
uint8_t cla;
uint8_t ins;
uint8_t p1;
uint8_t p2;
uint8_t lc;
} __attribute__((packed)) APDU_HEADER;
#define APDU_FIDO_U2F_REGISTER 0x01
#define APDU_FIDO_U2F_AUTHENTICATE 0x02
#define APDU_FIDO_U2F_VERSION 0x03
#define APDU_FIDO_NFCCTAP_MSG 0x10
#define APDU_INS_SELECT 0xA4
#define APDU_INS_READ_BINARY 0xB0
#define SW_SUCCESS 0x9000
#define SW_GET_RESPONSE 0x6100 // Command successfully executed; 'XX' bytes of data are available and can be requested using GET RESPONSE.
#define SW_WRONG_LENGTH 0x6700
#define SW_COND_USE_NOT_SATISFIED 0x6985
#define SW_FILE_NOT_FOUND 0x6a82
#define SW_INS_INVALID 0x6d00 // Instruction code not supported or invalid
#define SW_INTERNAL_EXCEPTION 0x6f00
#endif //_APDU_H_

View File

@ -60,7 +60,7 @@ static const uint8_t * _signing_key = NULL;
static int _key_len = 0;
// Secrets for testing only
static uint8_t master_secret[32];
static uint8_t master_secret[64];
static uint8_t transport_secret[32];
@ -73,13 +73,17 @@ void crypto_sha256_init()
void crypto_reset_master_secret()
{
ctap_generate_rng(master_secret, 32);
ctap_generate_rng(master_secret, 64);
ctap_generate_rng(transport_secret, 32);
}
void crypto_load_master_secret(uint8_t * key)
{
memmove(master_secret, key, 32);
memmove(transport_secret, key+32, 32);
#if KEY_SPACE_BYTES < 96
#error "need more key bytes"
#endif
memmove(master_secret, key, 64);
memmove(transport_secret, key+64, 32);
}
void crypto_sha256_update(uint8_t * data, size_t len)
@ -108,6 +112,11 @@ void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
key = master_secret;
klen = sizeof(master_secret);
}
else if (key == CRYPTO_TRANSPORT_KEY)
{
key = transport_secret;
klen = 32;
}
if(klen > 64)
{

View File

@ -19,6 +19,10 @@ void crypto_sha256_final(uint8_t * hash);
void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac);
void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac);
void crypto_sha512_init();
void crypto_sha512_update(const uint8_t * data, size_t len);
void crypto_sha512_final(uint8_t * hash);
void crypto_ecc256_init();
void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8_t * y);

View File

@ -21,10 +21,10 @@
#include "device.h"
#include APP_CONFIG
#include "wallet.h"
#include "extensions.h"
#include "device.h"
#define PIN_TOKEN_SIZE 16
uint8_t PIN_TOKEN[PIN_TOKEN_SIZE];
uint8_t KEY_AGREEMENT_PUB[64];
static uint8_t KEY_AGREEMENT_PRIV[32];
@ -33,6 +33,8 @@ static int8_t PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS;
AuthenticatorState STATE;
static void ctap_reset_key_agreement();
static struct {
CTAP_authDataHeader authData;
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
@ -66,6 +68,8 @@ uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash)
}
uint8_t ctap_get_info(CborEncoder * encoder)
{
int ret;
@ -74,16 +78,14 @@ uint8_t ctap_get_info(CborEncoder * encoder)
CborEncoder options;
CborEncoder pins;
const int number_of_versions = 2;
ret = cbor_encoder_create_map(encoder, &map, 5);
ret = cbor_encoder_create_map(encoder, &map, 6);
check_ret(ret);
{
ret = cbor_encode_uint(&map, RESP_versions); // versions key
check_ret(ret);
{
ret = cbor_encoder_create_array(&map, &array, number_of_versions);
ret = cbor_encoder_create_array(&map, &array, 2);
check_ret(ret);
{
ret = cbor_encode_text_stringz(&array, "U2F_V2");
@ -95,6 +97,19 @@ uint8_t ctap_get_info(CborEncoder * encoder)
check_ret(ret);
}
ret = cbor_encode_uint(&map, RESP_extensions);
check_ret(ret);
{
ret = cbor_encoder_create_array(&map, &array, 1);
check_ret(ret);
{
ret = cbor_encode_text_stringz(&array, "hmac-secret");
check_ret(ret);
}
ret = cbor_encoder_close_container(&map, &array);
check_ret(ret);
}
ret = cbor_encode_uint(&map, RESP_aaguid);
check_ret(ret);
{
@ -308,18 +323,123 @@ static int is_matching_rk(CTAP_residentKey * rk, CTAP_residentKey * rk2)
(rk->user.id_size == rk2->user.id_size);
}
static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf, unsigned int * ext_encoder_buf_size)
{
CborEncoder extensions;
int ret;
uint8_t output[64];
uint8_t shared_secret[32];
uint8_t hmac[32];
uint8_t credRandom[32];
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, unsigned int len, CTAP_userEntity * user, uint8_t credtype, int32_t algtype, int32_t * sz, int store)
if (ext->hmac_secret_present == EXT_HMAC_SECRET_PARSED)
{
printf1(TAG_CTAP, "Processing hmac-secret..\r\n");
crypto_ecc256_shared_secret((uint8_t*) &ext->hmac_secret.keyAgreement.pubkey,
KEY_AGREEMENT_PRIV,
shared_secret);
crypto_sha256_init();
crypto_sha256_update(shared_secret, 32);
crypto_sha256_final(shared_secret);
crypto_sha256_hmac_init(shared_secret, 32, hmac);
crypto_sha256_update(ext->hmac_secret.saltEnc, ext->hmac_secret.saltLen);
crypto_sha256_hmac_final(shared_secret, 32, hmac);
if (memcmp(ext->hmac_secret.saltAuth, hmac, 16) == 0)
{
printf1(TAG_CTAP, "saltAuth is valid\r\n");
}
else
{
printf1(TAG_CTAP, "saltAuth is invalid\r\n");
return CTAP2_ERR_EXTENSION_FIRST;
}
// Generate credRandom
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY, 0, credRandom);
crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId));
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY, 0, credRandom);
// Decrypt saltEnc
crypto_aes256_init(shared_secret, NULL);
crypto_aes256_decrypt(ext->hmac_secret.saltEnc, ext->hmac_secret.saltLen);
// Generate outputs
crypto_sha256_hmac_init(credRandom, 32, output);
crypto_sha256_update(ext->hmac_secret.saltEnc, 32);
crypto_sha256_hmac_final(credRandom, 32, output);
if (ext->hmac_secret.saltLen == 64)
{
crypto_sha256_hmac_init(credRandom, 32, output + 32);
crypto_sha256_update(ext->hmac_secret.saltEnc + 32, 32);
crypto_sha256_hmac_final(credRandom, 32, output + 32);
}
// Encrypt for final output
crypto_aes256_init(shared_secret, NULL);
crypto_aes256_encrypt(output, ext->hmac_secret.saltLen);
// output
printf1(TAG_GREEN, "have %d bytes for Extenstions encoder\r\n",*ext_encoder_buf_size);
cbor_encoder_init(&extensions, ext_encoder_buf, *ext_encoder_buf_size, 0);
{
CborEncoder hmac_secret_map;
ret = cbor_encoder_create_map(&extensions, &hmac_secret_map, 1);
check_ret(ret);
{
ret = cbor_encode_text_stringz(&hmac_secret_map, "hmac-secret");
check_ret(ret);
ret = cbor_encode_byte_string(&hmac_secret_map, output, ext->hmac_secret.saltLen);
check_ret(ret);
}
ret = cbor_encoder_close_container(&extensions, &hmac_secret_map);
check_ret(ret);
}
*ext_encoder_buf_size = cbor_encoder_get_buffer_size(&extensions, ext_encoder_buf);
}
else if (ext->hmac_secret_present == EXT_HMAC_SECRET_REQUESTED)
{
cbor_encoder_init(&extensions, ext_encoder_buf, *ext_encoder_buf_size, 0);
{
CborEncoder hmac_secret_map;
ret = cbor_encoder_create_map(&extensions, &hmac_secret_map, 1);
check_ret(ret);
{
ret = cbor_encode_text_stringz(&hmac_secret_map, "hmac-secret");
check_ret(ret);
ret = cbor_encode_boolean(&hmac_secret_map, 1);
check_ret(ret);
}
ret = cbor_encoder_close_container(&extensions, &hmac_secret_map);
check_ret(ret);
}
*ext_encoder_buf_size = cbor_encoder_get_buffer_size(&extensions, ext_encoder_buf);
}
else
{
*ext_encoder_buf_size = 0;
}
return 0;
}
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo)
{
CborEncoder cose_key;
int auth_data_sz, ret;
unsigned int auth_data_sz = sizeof(CTAP_authDataHeader);
uint32_t count;
CTAP_residentKey rk, rk2;
CTAP_authData * authData = (CTAP_authData *)auth_data_buf;
uint8_t * cose_key_buf = auth_data_buf + sizeof(CTAP_authData);
if((sizeof(CTAP_authDataHeader)) > len)
if((sizeof(CTAP_authDataHeader)) > *len)
{
printf1(TAG_ERR,"assertion fail, auth_data_buf must be at least %d bytes\n", sizeof(CTAP_authData) - sizeof(CTAP_attestHeader));
exit(1);
@ -335,7 +455,12 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
count = auth_data_update_count(&authData->head);
device_set_status(CTAPHID_STATUS_UPNEEDED);
int but = ctap_user_presence_test();
// if NFC - not need to click a button
int but = 1;
if(!device_is_nfc())
{
but = ctap_user_presence_test();
}
if (!but)
{
@ -351,13 +476,12 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
authData->head.flags |= (ctap_is_pin_set() << 2);
if (credtype != 0)
if (credInfo != NULL)
{
// add attestedCredentialData
authData->head.flags |= (1 << 6);//include attestation data
cbor_encoder_init(&cose_key, cose_key_buf, len - sizeof(CTAP_authData), 0);
cbor_encoder_init(&cose_key, cose_key_buf, *len - sizeof(CTAP_authData), 0);
memmove(authData->attest.aaguid, CTAP_AAGUID, 16);
authData->attest.credLenL = sizeof(CredentialId) & 0x00FF;
@ -375,10 +499,10 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
make_auth_tag(authData->head.rpIdHash, authData->attest.id.nonce, count, authData->attest.id.tag);
// resident key
if (store)
if (credInfo->rk)
{
memmove(&rk.id, &authData->attest.id, sizeof(CredentialId));
memmove(&rk.user, user, sizeof(CTAP_userEntity));
memmove(&rk.user, &credInfo->user, sizeof(CTAP_userEntity));
unsigned int index = STATE.rk_stored;
unsigned int i;
@ -402,29 +526,19 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
}
done_rk:
// DELETE
//crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL);
//crypto_aes256_encrypt((uint8_t*)&authData->attest.credential.user, CREDENTIAL_ENC_SIZE);
printf1(TAG_GREEN, "MADE credId: "); dump_hex1(TAG_GREEN, (uint8_t*) &authData->attest.id, sizeof(CredentialId));
ctap_generate_cose_key(&cose_key, (uint8_t*)&authData->attest.id, sizeof(CredentialId), credtype, algtype);
ctap_generate_cose_key(&cose_key, (uint8_t*)&authData->attest.id, sizeof(CredentialId), credInfo->publicKeyCredentialType, credInfo->COSEAlgorithmIdentifier);
auth_data_sz = sizeof(CTAP_authData) + cbor_encoder_get_buffer_size(&cose_key, cose_key_buf);
}
else
{
auth_data_sz = sizeof(CTAP_authDataHeader);
}
{
ret = cbor_encode_int(map,RESP_authData);
check_ret(ret);
ret = cbor_encode_byte_string(map, auth_data_buf, auth_data_sz);
check_ret(ret);
}
if (sz) *sz = auth_data_sz;
*len = auth_data_sz;
return 0;
}
@ -551,12 +665,13 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
CTAP_makeCredential MC;
int ret;
unsigned int i;
uint8_t auth_data_buf[300];
uint8_t auth_data_buf[310];
CTAP_credentialDescriptor * excl_cred = (CTAP_credentialDescriptor *) auth_data_buf;
uint8_t * sigbuf = auth_data_buf + 32;
uint8_t * sigder = auth_data_buf + 32 + 64;
ret = ctap_parse_make_credential(&MC,encoder,request,length);
if (ret != 0)
{
printf2(TAG_ERR,"error, parse_make_credential failed\n");
@ -611,19 +726,38 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
check_ret(ret);
}
CborEncoder map;
ret = cbor_encoder_create_map(encoder, &map, 3);
check_ret(ret);
int32_t auth_data_sz;
ret = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, sizeof(auth_data_buf),
&MC.user, MC.publicKeyCredentialType, MC.COSEAlgorithmIdentifier, &auth_data_sz, MC.rk);
uint32_t auth_data_sz = sizeof(auth_data_buf);
ret = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, &auth_data_sz,
&MC.credInfo);
check_retr(ret);
{
unsigned int ext_encoder_buf_size = sizeof(auth_data_buf) - auth_data_sz;
uint8_t * ext_encoder_buf = auth_data_buf + auth_data_sz;
ret = ctap_make_extensions(&MC.extensions, ext_encoder_buf, &ext_encoder_buf_size);
check_retr(ret);
if (ext_encoder_buf_size)
{
((CTAP_authData *)auth_data_buf)->head.flags |= (1 << 7);
auth_data_sz += ext_encoder_buf_size;
}
}
{
ret = cbor_encode_int(&map,RESP_authData);
check_ret(ret);
ret = cbor_encode_byte_string(&map, auth_data_buf, auth_data_sz);
check_ret(ret);
}
crypto_ecc256_load_attestation_key();
int sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_sz, MC.clientDataHash, auth_data_buf, sigbuf, sigder);
printf1(TAG_MC,"der sig [%d]: ", sigder_sz); dump_hex1(TAG_MC, sigder, sigder_sz);
ret = ctap_add_attest_statement(&map, sigder, sigder_sz);
@ -776,8 +910,19 @@ int ctap_filter_invalid_credentials(CTAP_getAssertion * GA)
if (! ctap_authenticate_credential(&GA->rp, &GA->creds[i]))
{
printf1(TAG_GA, "CRED #%d is invalid\n", GA->creds[i].credential.id.count);
#ifdef ENABLE_U2F_EXTENSIONS
if (is_extension_request((uint8_t*)&GA->creds[i].credential.id, sizeof(CredentialId)))
{
printf1(TAG_EXT, "CRED #%d is extension\n", GA->creds[i].credential.id.count);
count++;
}
else
#endif
{
GA->creds[i].credential.id.count = 0; // invalidate
}
}
else
{
// add user info if it exists
@ -803,7 +948,7 @@ int ctap_filter_invalid_credentials(CTAP_getAssertion * GA)
printf1(TAG_GA, "RK %d is a rpId match!\r\n", i);
if (count == ALLOW_LIST_MAX_SIZE-1)
{
printf2(TAG_ERR, "not enough ram allocated for matching RK's (%d)\r\n", count);
printf2(TAG_ERR, "not enough ram allocated for matching RK's (%d). Skipping.\r\n", count);
break;
}
GA->creds[count].type = PUB_KEY_CRED_PUB_KEY;
@ -832,6 +977,7 @@ static void save_credential_list(CTAP_authDataHeader * head, uint8_t * clientDat
memmove(getAssertionState.clientDataHash, clientDataHash, CLIENT_DATA_HASH_SIZE);
memmove(&getAssertionState.authData, head, sizeof(CTAP_authDataHeader));
memmove(getAssertionState.creds, creds, sizeof(CTAP_credentialDescriptor) * (count));
}
getAssertionState.count = count;
printf1(TAG_GA,"saved %d credentials\n",count);
@ -856,6 +1002,7 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr
int ret;
uint8_t sigbuf[64];
uint8_t sigder[72];
int sigder_sz;
if (add_user)
{
@ -869,7 +1016,16 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr
crypto_ecc256_load_key((uint8_t*)&cred->credential.id, sizeof(CredentialId), NULL, 0);
int sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(CTAP_authDataHeader), clientDataHash, auth_data_buf, sigbuf, sigder);
#ifdef ENABLE_U2F_EXTENSIONS
if ( extend_fido2(&cred->credential.id, sigder) )
{
sigder_sz = 72;
}
else
#endif
{
sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(CTAP_authDataHeader), clientDataHash, auth_data_buf, sigbuf, sigder);
}
{
ret = cbor_encode_int(map, RESP_signature);
@ -886,7 +1042,6 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
CborEncoder map;
CTAP_authDataHeader authData;
memmove(&authData, &getAssertionState.authData, sizeof(CTAP_authDataHeader));
// CTAP_authDataHeader * authData = &getAssertionState.authData;
CTAP_credentialDescriptor * cred = pop_credential();
@ -909,6 +1064,7 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
ret = cbor_encoder_create_map(encoder, &map, 3);
}
check_ret(ret);
printf1(TAG_RED, "RPID hash: "); dump_hex1(TAG_RED, authData.rpIdHash, 32);
@ -919,6 +1075,7 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
check_ret(ret);
}
// if only one account for this RP, null out the user details
if (!getAssertionState.user_verified)
{
@ -939,7 +1096,7 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
{
CTAP_getAssertion GA;
uint8_t auth_data_buf[sizeof(CTAP_authDataHeader)];
uint8_t auth_data_buf[sizeof(CTAP_authDataHeader) + 80];
int ret = ctap_parse_get_assertion(&GA,request,length);
if (ret != 0)
@ -948,19 +1105,15 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
return ret;
}
if (ctap_is_pin_set() && GA.pinAuthPresent == 0)
{
printf2(TAG_ERR,"pinAuth is required\n");
return CTAP2_ERR_PIN_REQUIRED;
}
else
{
if (ctap_is_pin_set() || (GA.pinAuthPresent))
if (GA.pinAuthPresent)
{
ret = verify_pin_auth(GA.pinAuth, GA.clientDataHash);
check_retr(ret);
getAssertionState.user_verified = 1;
}
else
{
getAssertionState.user_verified = 0;
}
if (!GA.rp.size || !GA.clientDataHashPresent)
@ -985,34 +1138,15 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
map_size += 1;
}
if (GA.extensions.hmac_secret_present == EXT_HMAC_SECRET_PARSED)
{
printf1(TAG_GA, "hmac-secret is present\r\n");
}
ret = cbor_encoder_create_map(encoder, &map, map_size);
check_ret(ret);
ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, sizeof(auth_data_buf), NULL, 0,0,NULL, 0);
check_retr(ret);
/*for (int j = 0; j < GA.credLen; j++)*/
/*{*/
/*printf1(TAG_GA,"CRED ID (# %d): ", GA.creds[j].credential.enc.count);*/
/*dump_hex1(TAG_GA, (uint8_t*)&GA.creds[j].credential, sizeof(struct Credential));*/
/*if (ctap_authenticate_credential(&GA.rp, &GA.creds[j])) // warning encryption will break this*/
/*{*/
/*printf1(TAG_GA," Authenticated.\n");*/
/*}*/
/*else*/
/*{*/
/*printf1(TAG_GA," NOT authentic.\n");*/
/*}*/
/*}*/
// Decrypt here
//
if (validCredCount > 0)
{
save_credential_list((CTAP_authDataHeader*)auth_data_buf, GA.clientDataHash, GA.creds, validCredCount-1); // skip last one
}
else
if (validCredCount == 0)
{
printf2(TAG_ERR,"Error, no authentic credential\n");
return CTAP2_ERR_NO_CREDENTIALS;
@ -1042,6 +1176,50 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
CTAP_credentialDescriptor * cred = &GA.creds[validCredCount - 1];
GA.extensions.hmac_secret.credential = &cred->credential;
#ifdef ENABLE_U2F_EXTENSIONS
if ( is_extension_request((uint8_t*)&GA.creds[validCredCount - 1].credential.id, sizeof(CredentialId)) )
{
ret = cbor_encode_int(&map,RESP_authData);
check_ret(ret);
memset(auth_data_buf,0,sizeof(CTAP_authDataHeader));
ret = cbor_encode_byte_string(&map, auth_data_buf, sizeof(CTAP_authDataHeader));
check_ret(ret);
}
else
#endif
{
uint32_t len = sizeof(auth_data_buf);
ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, &len, NULL);
check_retr(ret);
((CTAP_authData *)auth_data_buf)->head.flags &= ~(1 << 2);
((CTAP_authData *)auth_data_buf)->head.flags |= (getAssertionState.user_verified << 2);
{
unsigned int ext_encoder_buf_size = sizeof(auth_data_buf) - len;
uint8_t * ext_encoder_buf = auth_data_buf + len;
ret = ctap_make_extensions(&GA.extensions, ext_encoder_buf, &ext_encoder_buf_size);
check_retr(ret);
if (ext_encoder_buf_size)
{
((CTAP_authData *)auth_data_buf)->head.flags |= (1 << 7);
len += ext_encoder_buf_size;
}
}
{
ret = cbor_encode_int(&map,RESP_authData);
check_ret(ret);
ret = cbor_encode_byte_string(&map, auth_data_buf, len);
check_ret(ret);
}
}
save_credential_list((CTAP_authDataHeader*)auth_data_buf, GA.clientDataHash, GA.creds, validCredCount-1); // skip last one
ret = ctap_end_get_assertion(&map, cred, auth_data_buf, GA.clientDataHash, add_user_info);
check_retr(ret);
@ -1147,7 +1325,7 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor
crypto_aes256_decrypt(pinHashEnc, 16);
if (memcmp(pinHashEnc, PIN_CODE_HASH, 16) != 0)
{
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
ctap_reset_key_agreement();
ctap_decrement_pin_attempts();
if (ctap_device_boot_locked())
{
@ -1190,7 +1368,7 @@ uint8_t ctap_add_pin_if_verified(uint8_t * pinTokenEnc, uint8_t * platform_pubke
printf2(TAG_ERR,"platform-pubkey: "); dump_hex1(TAG_ERR, platform_pubkey, 64);
printf2(TAG_ERR,"device-pubkey: "); dump_hex1(TAG_ERR, KEY_AGREEMENT_PUB, 64);
// Generate new keyAgreement pair
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
ctap_reset_key_agreement();
ctap_decrement_pin_attempts();
if (ctap_device_boot_locked())
{
@ -1215,6 +1393,7 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length)
uint8_t pinTokenEnc[PIN_TOKEN_SIZE];
int ret = ctap_parse_client_pin(&CP,request,length);
switch(CP.subCommand)
{
case CP_cmdSetPin:
@ -1551,12 +1730,16 @@ void ctap_init()
exit(1);
}
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
if (! device_is_nfc())
{
ctap_reset_key_agreement();
}
#ifdef BRIDGE_TO_WALLET
wallet_init();
#endif
}
uint8_t ctap_is_pin_set()
@ -1747,7 +1930,10 @@ int8_t ctap_load_key(uint8_t index, uint8_t * key)
return 0;
}
static void ctap_reset_key_agreement()
{
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
}
void ctap_reset()
{
@ -1764,7 +1950,7 @@ void ctap_reset()
ctap_reset_state();
memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH));
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
ctap_reset_key_agreement();
crypto_reset_master_secret();
}

View File

@ -54,6 +54,13 @@
#define CP_getKeyAgreement 0x07
#define CP_getRetries 0x08
#define EXT_HMAC_SECRET_COSE_KEY 0x01
#define EXT_HMAC_SECRET_SALT_ENC 0x02
#define EXT_HMAC_SECRET_SALT_AUTH 0x03
#define EXT_HMAC_SECRET_REQUESTED 0x01
#define EXT_HMAC_SECRET_PARSED 0x02
#define RESP_versions 0x1
#define RESP_extensions 0x2
#define RESP_aaguid 0x3
@ -142,9 +149,13 @@ struct Credential {
CredentialId id;
CTAP_userEntity user;
};
typedef struct Credential CTAP_residentKey;
typedef struct
{
uint8_t type;
struct Credential credential;
} CTAP_credentialDescriptor;
typedef struct
{
@ -181,34 +192,62 @@ struct rpId
uint8_t name[RP_NAME_LIMIT];
};
typedef struct
{
struct{
uint8_t x[32];
uint8_t y[32];
} pubkey;
int kty;
int crv;
} COSE_key;
typedef struct
{
uint8_t saltLen;
uint8_t saltEnc[64];
uint8_t saltAuth[32];
COSE_key keyAgreement;
struct Credential * credential;
} CTAP_hmac_secret;
typedef struct
{
uint8_t hmac_secret_present;
CTAP_hmac_secret hmac_secret;
} CTAP_extensions;
typedef struct
{
CTAP_userEntity user;
uint8_t publicKeyCredentialType;
int32_t COSEAlgorithmIdentifier;
uint8_t rk;
} CTAP_credInfo;
typedef struct
{
uint32_t paramsParsed;
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
struct rpId rp;
CTAP_userEntity user;
uint8_t publicKeyCredentialType;
int32_t COSEAlgorithmIdentifier;
CTAP_credInfo credInfo;
CborValue excludeList;
size_t excludeListSize;
uint8_t rk;
uint8_t uv;
uint8_t up;
uint8_t pinAuth[16];
uint8_t pinAuthPresent;
int pinProtocol;
CTAP_extensions extensions;
} CTAP_makeCredential;
typedef struct
{
uint8_t type;
struct Credential credential;
} CTAP_credentialDescriptor;
typedef struct
{
@ -230,22 +269,16 @@ typedef struct
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
uint8_t allowListPresent;
CTAP_extensions extensions;
} CTAP_getAssertion;
typedef struct
{
int pinProtocol;
int subCommand;
struct
{
struct{
uint8_t x[32];
uint8_t y[32];
} pubkey;
int kty;
int crv;
} keyAgreement;
COSE_key keyAgreement;
uint8_t keyAgreementPresent;
uint8_t pinAuth[16];
uint8_t pinAuthPresent;

View File

@ -128,14 +128,14 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
}
sz = USER_ID_MAX_SIZE;
ret = cbor_value_copy_byte_string(&map, MC->user.id, &sz, NULL);
ret = cbor_value_copy_byte_string(&map, MC->credInfo.user.id, &sz, NULL);
if (ret == CborErrorOutOfMemory)
{
printf2(TAG_ERR,"Error, USER_ID is too large\n");
return CTAP2_ERR_LIMIT_EXCEEDED;
}
MC->user.id_size = sz;
printf1(TAG_GREEN,"parsed id_size: %d\r\n", MC->user.id_size);
MC->credInfo.user.id_size = sz;
printf1(TAG_GREEN,"parsed id_size: %d\r\n", MC->credInfo.user.id_size);
check_ret(ret);
}
else if (strcmp((const char *)key, "name") == 0)
@ -146,12 +146,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
sz = USER_NAME_LIMIT;
ret = cbor_value_copy_text_string(&map, (char *)MC->user.name, &sz, NULL);
ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.name, &sz, NULL);
if (ret != CborErrorOutOfMemory)
{ // Just truncate the name it's okay
check_ret(ret);
}
MC->user.name[USER_NAME_LIMIT - 1] = 0;
MC->credInfo.user.name[USER_NAME_LIMIT - 1] = 0;
}
else if (strcmp((const char *)key, "displayName") == 0)
{
@ -161,12 +161,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
sz = DISPLAY_NAME_LIMIT;
ret = cbor_value_copy_text_string(&map, (char *)MC->user.displayName, &sz, NULL);
ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.displayName, &sz, NULL);
if (ret != CborErrorOutOfMemory)
{ // Just truncate the name it's okay
check_ret(ret);
}
MC->user.displayName[DISPLAY_NAME_LIMIT - 1] = 0;
MC->credInfo.user.displayName[DISPLAY_NAME_LIMIT - 1] = 0;
}
else if (strcmp((const char *)key, "icon") == 0)
{
@ -176,12 +176,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
sz = ICON_LIMIT;
ret = cbor_value_copy_text_string(&map, (char *)MC->user.icon, &sz, NULL);
ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.icon, &sz, NULL);
if (ret != CborErrorOutOfMemory)
{ // Just truncate the name it's okay
check_ret(ret);
}
MC->user.icon[ICON_LIMIT - 1] = 0;
MC->credInfo.user.icon[ICON_LIMIT - 1] = 0;
}
else
@ -305,8 +305,8 @@ uint8_t parse_pub_key_cred_params(CTAP_makeCredential * MC, CborValue * val)
{
if (pub_key_cred_param_supported(cred_type, alg_type) == CREDENTIAL_IS_SUPPORTED)
{
MC->publicKeyCredentialType = cred_type;
MC->COSEAlgorithmIdentifier = alg_type;
MC->credInfo.publicKeyCredentialType = cred_type;
MC->credInfo.COSEAlgorithmIdentifier = alg_type;
MC->paramsParsed |= PARAM_pubKeyCredParams;
return 0;
}
@ -521,7 +521,7 @@ uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up)
if (cbor_value_get_type(&map) != CborBooleanType)
{
printf2(TAG_ERR,"Error, expecting text string type for rp map value\n");
printf2(TAG_ERR,"Error, expecting bool type for option map value\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
@ -556,6 +556,154 @@ uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up)
return 0;
}
uint8_t ctap_parse_hmac_secret(CborValue * val, CTAP_hmac_secret * hs)
{
size_t map_length;
size_t salt_len;
uint8_t parsed_count = 0;
int key;
int ret;
unsigned int i;
CborValue map;
if (cbor_value_get_type(val) != CborMapType)
{
printf2(TAG_ERR,"error, wrong type\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_enter_container(val,&map);
check_ret(ret);
ret = cbor_value_get_map_length(val, &map_length);
check_ret(ret);
for (i = 0; i < map_length; i++)
{
if (cbor_value_get_type(&map) != CborIntegerType)
{
printf2(TAG_ERR,"Error, expecting CborIntegerTypefor hmac-secret map key, got %s\n", cbor_value_get_type_string(&map));
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_get_int(&map, &key);
check_ret(ret);
ret = cbor_value_advance(&map);
check_ret(ret);
switch(key)
{
case EXT_HMAC_SECRET_COSE_KEY:
ret = parse_cose_key(&map, &hs->keyAgreement);
check_retr(ret);
parsed_count++;
break;
case EXT_HMAC_SECRET_SALT_ENC:
salt_len = 64;
ret = cbor_value_copy_byte_string(&map, hs->saltEnc, &salt_len, NULL);
if ((salt_len != 32 && salt_len != 64) || ret == CborErrorOutOfMemory)
{
return CTAP1_ERR_INVALID_LENGTH;
}
check_ret(ret);
hs->saltLen = salt_len;
parsed_count++;
break;
case EXT_HMAC_SECRET_SALT_AUTH:
salt_len = 32;
ret = cbor_value_copy_byte_string(&map, hs->saltAuth, &salt_len, NULL);
check_ret(ret);
parsed_count++;
break;
}
ret = cbor_value_advance(&map);
check_ret(ret);
}
if (parsed_count != 3)
{
printf2(TAG_ERR, "ctap_parse_hmac_secret missing parameter. Got %d.\r\n", parsed_count);
return CTAP2_ERR_MISSING_PARAMETER;
}
return 0;
}
uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext)
{
CborValue map;
size_t sz, map_length;
char key[16];
int ret;
unsigned int i;
bool b;
if (cbor_value_get_type(val) != CborMapType)
{
printf2(TAG_ERR,"error, wrong type\n");
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = cbor_value_enter_container(val, &map);
check_ret(ret);
ret = cbor_value_get_map_length(val, &map_length);
check_ret(ret);
for (i = 0; i < map_length; i++)
{
if (cbor_value_get_type(&map) != CborTextStringType)
{
printf2(TAG_ERR,"Error, expecting text string type for options map key, got %s\n", cbor_value_get_type_string(&map));
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
sz = sizeof(key);
ret = cbor_value_copy_text_string(&map, key, &sz, NULL);
if (ret == CborErrorOutOfMemory)
{
printf2(TAG_ERR,"Error, rp map key is too large. Ignoring.\n");
cbor_value_advance(&map);
cbor_value_advance(&map);
continue;
}
check_ret(ret);
key[sizeof(key) - 1] = 0;
ret = cbor_value_advance(&map);
check_ret(ret);
if (strncmp(key, "hmac-secret",11) == 0)
{
if (cbor_value_get_type(&map) == CborBooleanType)
{
ret = cbor_value_get_boolean(&map, &b);
check_ret(ret);
if (b) ext->hmac_secret_present = EXT_HMAC_SECRET_REQUESTED;
printf1(TAG_CTAP, "set hmac_secret_present to %d\r\n", b);
}
else if (cbor_value_get_type(&map) == CborMapType)
{
ret = ctap_parse_hmac_secret(&map, &ext->hmac_secret);
check_retr(ret);
ext->hmac_secret_present = EXT_HMAC_SECRET_PARSED;
printf1(TAG_CTAP, "parsed hmac_secret request\r\n");
}
else
{
printf1(TAG_RED, "warning: hmac_secret request ignored for being wrong type\r\n");
}
}
ret = cbor_value_advance(&map);
check_ret(ret);
}
return 0;
}
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length)
{
int ret;
@ -631,8 +779,8 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
ret = parse_user(MC, &map);
printf1(TAG_MC," ID: "); dump_hex1(TAG_MC, MC->user.id, MC->user.id_size);
printf1(TAG_MC," name: %s\n", MC->user.name);
printf1(TAG_MC," ID: "); dump_hex1(TAG_MC, MC->credInfo.user.id, MC->credInfo.user.id_size);
printf1(TAG_MC," name: %s\n", MC->credInfo.user.name);
break;
case MC_pubKeyCredParams:
@ -640,8 +788,8 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
ret = parse_pub_key_cred_params(MC, &map);
printf1(TAG_MC," cred_type: 0x%02x\n", MC->publicKeyCredentialType);
printf1(TAG_MC," alg_type: %d\n", MC->COSEAlgorithmIdentifier);
printf1(TAG_MC," cred_type: 0x%02x\n", MC->credInfo.publicKeyCredentialType);
printf1(TAG_MC," alg_type: %d\n", MC->credInfo.COSEAlgorithmIdentifier);
break;
case MC_excludeList:
@ -665,11 +813,13 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
{
return CTAP2_ERR_INVALID_CBOR_TYPE;
}
ret = ctap_parse_extensions(&map, &MC->extensions);
check_retr(ret);
break;
case MC_options:
printf1(TAG_MC,"CTAP_options\n");
ret = parse_options(&map, &MC->rk, &MC->uv, &MC->up);
ret = parse_options(&map, &MC->credInfo.rk, &MC->uv, &MC->up);
check_retr(ret);
break;
case MC_pinAuth:
@ -886,6 +1036,8 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
break;
case GA_extensions:
printf1(TAG_GA,"GA_extensions\n");
ret = ctap_parse_extensions(&map, &GA->extensions);
check_retr(ret);
break;
case GA_options:
@ -940,15 +1092,15 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
return 0;
}
uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int * crv)
uint8_t parse_cose_key(CborValue * it, COSE_key * cose)
{
CborValue map;
size_t map_length;
int ret,key;
unsigned int i;
int xkey = 0,ykey = 0;
*kty = 0;
*crv = 0;
cose->kty = 0;
cose->crv = 0;
CborType type = cbor_value_get_type(it);
@ -986,7 +1138,7 @@ uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int
printf1(TAG_PARSE,"COSE_KEY_LABEL_KTY\n");
if (cbor_value_get_type(&map) == CborIntegerType)
{
ret = cbor_value_get_int_checked(&map, kty);
ret = cbor_value_get_int_checked(&map, &cose->kty);
check_ret(ret);
}
else
@ -1001,7 +1153,7 @@ uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int
printf1(TAG_PARSE,"COSE_KEY_LABEL_CRV\n");
if (cbor_value_get_type(&map) == CborIntegerType)
{
ret = cbor_value_get_int_checked(&map, crv);
ret = cbor_value_get_int_checked(&map, &cose->crv);
check_ret(ret);
}
else
@ -1011,14 +1163,14 @@ uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int
break;
case COSE_KEY_LABEL_X:
printf1(TAG_PARSE,"COSE_KEY_LABEL_X\n");
ret = parse_fixed_byte_string(&map, x, 32);
ret = parse_fixed_byte_string(&map, cose->pubkey.x, 32);
check_retr(ret);
xkey = 1;
break;
case COSE_KEY_LABEL_Y:
printf1(TAG_PARSE,"COSE_KEY_LABEL_Y\n");
ret = parse_fixed_byte_string(&map, y, 32);
ret = parse_fixed_byte_string(&map, cose->pubkey.y, 32);
check_retr(ret);
ykey = 1;
@ -1030,7 +1182,7 @@ uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int
ret = cbor_value_advance(&map);
check_ret(ret);
}
if (xkey == 0 || ykey == 0 || *kty == 0 || *crv == 0)
if (xkey == 0 || ykey == 0 || cose->kty == 0 || cose->crv == 0)
{
return CTAP2_ERR_MISSING_PARAMETER;
}
@ -1110,7 +1262,7 @@ uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length
break;
case CP_keyAgreement:
printf1(TAG_CP,"CP_keyAgreement\n");
ret = parse_cose_key(&map, CP->keyAgreement.pubkey.x, CP->keyAgreement.pubkey.y, &CP->keyAgreement.kty, &CP->keyAgreement.crv);
ret = parse_cose_key(&map, &CP->keyAgreement);
check_retr(ret);
CP->keyAgreementPresent = 1;
break;

View File

@ -30,7 +30,7 @@ uint8_t parse_rp(struct rpId * rp, CborValue * val);
uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up);
uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it);
uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int * crv);
uint8_t parse_cose_key(CborValue * it, COSE_key * cose);
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length);

View File

@ -16,6 +16,12 @@
#include "util.h"
#include "log.h"
#include "extensions.h"
// move custom SHA512 command out,
// and the following headers too
#include "sha2.h"
#include "crypto.h"
#include APP_CONFIG
typedef enum
@ -528,6 +534,10 @@ static int ctaphid_buffer_packet(uint8_t * pkt_raw, uint8_t * cmd, uint32_t * ci
return buffer_status();
}
extern void _check_ret(CborError ret, int line, const char * filename);
#define check_hardcore(r) _check_ret(r,__LINE__, __FILE__);\
if ((r) != CborNoError) exit(1);
uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
{
uint8_t cmd;
@ -718,6 +728,155 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
#endif
#if defined(SOLO_HACKER) && (DEBUG_LEVEL > 0) && (!IS_BOOTLOADER == 1)
case CTAPHID_PROBE:
/*
* Expects CBOR-serialized data of the form
* {"subcommand": "hash_type", "data": b"the_data"}
* with hash_type in SHA256, SHA512
*/
// some random logging
printf1(TAG_HID,"CTAPHID_PROBE\n");
// initialise CTAP response object
ctap_response_init(&ctap_resp);
// initialise write buffer
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_PROBE;
// prepare parsing (or halt)
int ret;
CborParser parser;
CborValue it, map;
ret = cbor_parser_init(
ctap_buffer, (size_t) buffer_len(),
// strictly speaking, CTAP is not RFC canonical...
CborValidateCanonicalFormat,
&parser, &it);
check_hardcore(ret);
CborType type = cbor_value_get_type(&it);
if (type != CborMapType) exit(1);
ret = cbor_value_enter_container(&it,&map);
check_hardcore(ret);
size_t map_length = 0;
ret = cbor_value_get_map_length(&it, &map_length);
if (map_length != 2) exit(1);
// parse subcommand (or halt)
CborValue val;
ret = cbor_value_map_find_value(&it, "subcommand", &val);
check_hardcore(ret);
if (!cbor_value_is_text_string(&val))
exit(1);
int sha_version = 0;
bool found = false;
if (!found) {
ret = cbor_value_text_string_equals(
&val, "SHA256", &found);
check_hardcore(ret);
if (found)
sha_version = 256;
}
if (!found) {
ret = cbor_value_text_string_equals(
&val, "SHA512", &found);
check_hardcore(ret);
if (found)
sha_version = 512;
}
if (sha_version == 0)
exit(1);
// parse data (or halt)
ret = cbor_value_map_find_value(&it, "data", &val);
check_hardcore(ret);
if (!cbor_value_is_byte_string(&val))
exit(1);
size_t data_length = 0;
ret = cbor_value_calculate_string_length(&val, &data_length);
check_hardcore(ret);
if (data_length > 6*1024)
exit(1);
unsigned char data[6*1024];
ret = cbor_value_copy_byte_string (
&val, &data[0], &data_length, &val);
check_hardcore(ret);
// execute subcommand
if (sha_version == 256) {
// calculate hash
crypto_sha256_init();
crypto_sha256_update(data, data_length);
crypto_sha256_final(ctap_buffer);
// write output
wb.bcnt = CF_SHA256_HASHSZ; // 32 bytes
ctaphid_write(&wb, &ctap_buffer, CF_SHA256_HASHSZ);
}
if (sha_version == 512) {
// calculate hash
crypto_sha512_init();
crypto_sha512_update(data, data_length);
crypto_sha512_final(ctap_buffer);
// write output
wb.bcnt = CF_SHA512_HASHSZ; // 64 bytes
ctaphid_write(&wb, &ctap_buffer, CF_SHA512_HASHSZ);
}
// finalize
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
/*
case CTAPHID_SHA256:
// some random logging
printf1(TAG_HID,"CTAPHID_SHA256\n");
// initialise CTAP response object
ctap_response_init(&ctap_resp);
// initialise write buffer
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_SHA256;
wb.bcnt = CF_SHA256_HASHSZ; // 32 bytes
// calculate hash
crypto_sha256_init();
crypto_sha256_update(ctap_buffer, buffer_len());
crypto_sha256_final(ctap_buffer);
// copy to output
ctaphid_write(&wb, &ctap_buffer, CF_SHA256_HASHSZ);
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
case CTAPHID_SHA512:
// some random logging
printf1(TAG_HID,"CTAPHID_SHA512\n");
// initialise CTAP response object
ctap_response_init(&ctap_resp);
// initialise write buffer
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_SHA512;
wb.bcnt = CF_SHA512_HASHSZ; // 64 bytes
// calculate hash
crypto_sha512_init();
crypto_sha512_update(ctap_buffer, buffer_len());
crypto_sha512_final(ctap_buffer);
// copy to output
ctaphid_write(&wb, &ctap_buffer, CF_SHA512_HASHSZ);
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
*/
#endif
default:
printf2(TAG_ERR,"error, unimplemented HID cmd: %02x\r\n", buffer_cmd());

View File

@ -28,6 +28,8 @@
#define CTAPHID_ENTERBOOT (TYPE_INIT | 0x51)
#define CTAPHID_ENTERSTBOOT (TYPE_INIT | 0x52)
#define CTAPHID_GETRNG (TYPE_INIT | 0x60)
// reserved for debug, not implemented except for HACKER and DEBUG_LEVEl > 0
#define CTAPHID_PROBE (TYPE_INIT | 0x70)
#define ERR_INVALID_CMD 0x01
#define ERR_INVALID_PAR 0x02

View File

@ -86,5 +86,22 @@ void boot_st_bootloader();
// HID wink command
void device_wink();
typedef enum {
DEVICE_LOW_POWER_IDLE = 0,
DEVICE_LOW_POWER_FAST = 1,
DEVICE_FAST = 2,
} DEVICE_CLOCK_RATE;
// Set the clock rate for the device.
// Three modes are targetted for Solo.
// 0: Lowest clock rate for NFC.
// 1: fastest clock rate supported at a low power setting for NFC FIDO.
// 2: fastest clock rate. Generally for USB interface.
void device_set_clock_rate(DEVICE_CLOCK_RATE param);
// Returns 1 if operating in NFC mode.
// 0 otherwise.
bool device_is_nfc();
#endif

View File

@ -8,6 +8,7 @@
#include <stdint.h>
#include "extensions.h"
#include "u2f.h"
#include "ctap.h"
#include "wallet.h"
#include "solo.h"
#include "device.h"
@ -57,7 +58,8 @@ int16_t bridge_u2f_to_extensions(uint8_t * _chal, uint8_t * _appid, uint8_t klen
#elif defined(WALLET_EXTENSION)
ret = bridge_u2f_to_wallet(_chal, _appid, klen, keyh);
#else
ret = bridge_u2f_to_solo(_chal, _appid, klen, keyh);
ret = bridge_u2f_to_solo(sig, keyh, klen);
u2f_response_writeback(sig,72);
#endif
if (ret != 0)
@ -74,10 +76,25 @@ int16_t bridge_u2f_to_extensions(uint8_t * _chal, uint8_t * _appid, uint8_t klen
return U2F_SW_NO_ERROR;
}
int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len)
// Returns 1 if this is a extension request.
// Else 0 if nothing is done.
int16_t extend_fido2(CredentialId * credid, uint8_t * output)
{
if (is_extension_request((uint8_t*)credid, sizeof(CredentialId)))
{
output[0] = bridge_u2f_to_solo(output+1, (uint8_t*)credid, sizeof(CredentialId));
return 1;
}
else
{
return 0;
}
}
int16_t extend_u2f(APDU_HEADER * req, uint8_t * payload, uint32_t len)
{
struct u2f_authenticate_request * auth = (struct u2f_authenticate_request *) req->payload;
struct u2f_authenticate_request * auth = (struct u2f_authenticate_request *) payload;
uint16_t rcode;
if (req->ins == U2F_AUTHENTICATE)
@ -93,7 +110,7 @@ int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len)
{
rcode = U2F_SW_WRONG_DATA;
}
printf1(TAG_EXT,"Ignoring U2F request\n");
printf1(TAG_EXT,"Ignoring U2F check request\n");
dump_hex1(TAG_EXT, (uint8_t *) &auth->kh, auth->khl);
goto end;
}
@ -101,8 +118,8 @@ int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len)
{
if ( ! is_extension_request((uint8_t *) &auth->kh, auth->khl)) // Pin requests
{
rcode = U2F_SW_WRONG_PAYLOAD;
printf1(TAG_EXT, "Ignoring U2F request\n");
rcode = U2F_SW_WRONG_DATA;
printf1(TAG_EXT, "Ignoring U2F auth request\n");
dump_hex1(TAG_EXT, (uint8_t *) &auth->kh, auth->khl);
goto end;
}

View File

@ -7,9 +7,14 @@
#ifndef EXTENSIONS_H_
#define EXTENSIONS_H_
#include "u2f.h"
#include "apdu.h"
int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len);
int16_t extend_u2f(APDU_HEADER * req, uint8_t * payload, uint32_t len);
int16_t extend_fido2(CredentialId * credid, uint8_t * output);
int bootloader_bridge(int klen, uint8_t * keyh);
int is_extension_request(uint8_t * kh, int len);
#endif /* EXTENSIONS_H_ */

View File

@ -31,27 +31,26 @@
#include "log.h"
#include APP_CONFIG
int16_t bridge_u2f_to_solo(uint8_t * _chal, uint8_t * _appid, uint8_t klen, uint8_t * keyh)
// output must be at least 71 bytes
int16_t bridge_u2f_to_solo(uint8_t * output, uint8_t * keyh, int keylen)
{
static uint8_t msg_buf[72];
int8_t ret = 0;
wallet_request * req = (wallet_request *) keyh;
printf1(TAG_WALLET, "u2f-solo [%d]: ", klen); dump_hex1(TAG_WALLET, keyh, klen);
printf1(TAG_WALLET, "u2f-solo [%d]: ", keylen); dump_hex1(TAG_WALLET, keyh, keylen);
switch(req->operation)
{
case WalletVersion:
msg_buf[0] = SOLO_VERSION_MAJ;
msg_buf[1] = SOLO_VERSION_MIN;
msg_buf[2] = SOLO_VERSION_PATCH;
u2f_response_writeback(msg_buf, 3);
output[0] = SOLO_VERSION_MAJ;
output[1] = SOLO_VERSION_MIN;
output[2] = SOLO_VERSION_PATCH;
break;
case WalletRng:
printf1(TAG_WALLET,"SoloRng\n");
ret = ctap_generate_rng(msg_buf, 72);
ret = ctap_generate_rng(output, 71);
if (ret != 1)
{
printf1(TAG_WALLET,"Rng failed\n");
@ -60,7 +59,6 @@ int16_t bridge_u2f_to_solo(uint8_t * _chal, uint8_t * _appid, uint8_t klen, uint
}
ret = 0;
u2f_response_writeback((uint8_t *)msg_buf,72);
break;
default:

View File

@ -22,6 +22,6 @@
#ifndef SOLO_H_
#define SOLO_H_
int16_t bridge_u2f_to_solo(uint8_t * _chal, uint8_t * _appid, uint8_t klen, uint8_t * keyh);
int16_t bridge_u2f_to_solo(uint8_t * output, uint8_t * keyh, int keylen);
#endif

View File

@ -47,7 +47,9 @@ struct logtag tagtable[] = {
{TAG_WALLET,"WALLET"},
{TAG_STOR,"STOR"},
{TAG_BOOT,"BOOT"},
{TAG_BOOT,"EXT"},
{TAG_EXT,"EXT"},
{TAG_NFC,"NFC"},
{TAG_NFC_APDU, "NAPDU"},
};
@ -68,7 +70,7 @@ void LOG(uint32_t tag, const char * filename, int num, const char * fmt, ...)
{
if (tag & tagtable[i].tagn)
{
if (tagtable[i].tag[0]) printf("[%s] ", tagtable[i].tag);
if (tagtable[i].tag[0] && !(tag & TAG_NO_TAG)) printf("[%s] ", tagtable[i].tag);
i = 0;
break;
}

View File

@ -28,22 +28,25 @@ typedef enum
TAG_GA = (1 << 2),
TAG_CP = (1 << 3),
TAG_ERR = (1 << 4),
TAG_PARSE= (1 << 5),
TAG_PARSE = (1 << 5),
TAG_CTAP = (1 << 6),
TAG_U2F = (1 << 7),
TAG_DUMP = (1 << 8),
TAG_GREEN = (1 << 9),
TAG_RED= (1 << 10),
TAG_TIME= (1 << 11),
TAG_RED = (1 << 10),
TAG_TIME = (1 << 11),
TAG_HID = (1 << 12),
TAG_USB = (1 << 13),
TAG_WALLET = (1 << 14),
TAG_STOR = (1 << 15),
TAG_DUMP2 = (1 << 16),
TAG_BOOT = (1 << 17),
TAG_EXT = (1 << 17),
TAG_EXT = (1 << 18),
TAG_NFC = (1 << 19),
TAG_NFC_APDU = (1 << 20),
TAG_FILENO = (1u << 31)
TAG_NO_TAG = (1UL << 30),
TAG_FILENO = (1UL << 31)
} LOG_TAG;
#if DEBUG_LEVEL > 0

View File

@ -26,39 +26,32 @@ int main()
set_logging_mask(
/*0*/
// TAG_GEN|
// TAG_MC |
// TAG_GA |
TAG_WALLET |
//TAG_GEN|
//TAG_MC |
//TAG_GA |
//TAG_WALLET |
TAG_STOR |
// TAG_CP |
// TAG_CTAP|
// TAG_HID|
/*TAG_U2F|*/
// TAG_PARSE |
// TAG_TIME|
// TAG_DUMP|
//TAG_NFC_APDU |
TAG_NFC |
//TAG_CP |
//TAG_CTAP|
//TAG_HID|
//TAG_U2F|
//TAG_PARSE |
//TAG_TIME|
//TAG_DUMP|
TAG_GREEN|
TAG_RED|
TAG_ERR
);
device_init();
printf1(TAG_GEN,"init device\n");
usbhid_init();
printf1(TAG_GEN,"init usb\n");
ctaphid_init();
printf1(TAG_GEN,"init ctaphid\n");
ctap_init();
printf1(TAG_GEN,"init ctap\n");
memset(hidmsg,0,sizeof(hidmsg));
printf1(TAG_GEN,"recv'ing hid msg \n");
// printf1(TAG_GEN,"recv'ing hid msg \n");
while(1)
@ -80,6 +73,7 @@ int main()
{
}
ctaphid_check_timeouts();
}
// Should never get here

View File

@ -10,6 +10,7 @@
#include "crypto.h"
#include "log.h"
#include "device.h"
#include "apdu.h"
#include "wallet.h"
#ifdef ENABLE_U2F_EXTENSIONS
#include "extensions.h"
@ -27,12 +28,12 @@ void u2f_reset_response();
static CTAP_RESPONSE * _u2f_resp = NULL;
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
void u2f_request_ex(APDU_HEADER *req, uint8_t *payload, uint32_t len, CTAP_RESPONSE * resp)
{
uint16_t rcode = 0;
uint32_t len = ((req->LC3) | ((uint32_t)req->LC2 << 8) | ((uint32_t)req->LC1 << 16));
uint8_t byte;
ctap_response_init(resp);
u2f_set_writeback_buffer(resp);
if (req->cla != 0)
@ -42,9 +43,9 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
goto end;
}
#ifdef ENABLE_U2F_EXTENSIONS
rcode = extend_u2f(req, len);
rcode = extend_u2f(req, payload, len);
#endif
if (rcode != U2F_SW_NO_ERROR) // If the extension didn't do anything...
if (rcode != U2F_SW_NO_ERROR && rcode != U2F_SW_CONDITIONS_NOT_SATISFIED) // If the extension didn't do anything...
{
#ifdef ENABLE_U2F
switch(req->ins)
@ -59,7 +60,7 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
{
timestamp();
rcode = u2f_register((struct u2f_register_request*)req->payload);
rcode = u2f_register((struct u2f_register_request*)payload);
printf1(TAG_TIME,"u2f_register time: %d ms\n", timestamp());
}
@ -67,7 +68,7 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
case U2F_AUTHENTICATE:
printf1(TAG_U2F, "U2F_AUTHENTICATE\n");
timestamp();
rcode = u2f_authenticate((struct u2f_authenticate_request*)req->payload, req->p1);
rcode = u2f_authenticate((struct u2f_authenticate_request*)payload, req->p1);
printf1(TAG_TIME,"u2f_authenticate time: %d ms\n", timestamp());
break;
case U2F_VERSION:
@ -109,6 +110,22 @@ end:
printf1(TAG_U2F,"u2f resp: "); dump_hex1(TAG_U2F, _u2f_resp->data, _u2f_resp->length);
}
void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp)
{
if (len < 5 || !req)
return;
uint32_t alen = req[4];
u2f_request_ex((APDU_HEADER *)req, &req[5], alen, resp);
}
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
{
uint32_t len = ((req->LC3) | ((uint32_t)req->LC2 << 8) | ((uint32_t)req->LC1 << 16));
u2f_request_ex((APDU_HEADER *)req, req->payload, len, resp);
}
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len)
{
@ -156,7 +173,7 @@ static void u2f_make_auth_tag(struct u2f_key_handle * kh, uint8_t * appid, uint8
memmove(tag, hashbuf, CREDENTIAL_TAG_SIZE);
}
static int8_t u2f_new_keypair(struct u2f_key_handle * kh, uint8_t * appid, uint8_t * pubkey)
int8_t u2f_new_keypair(struct u2f_key_handle * kh, uint8_t * appid, uint8_t * pubkey)
{
ctap_generate_rng(kh->key, U2F_KEY_HANDLE_KEY_SIZE);
u2f_make_auth_tag(kh, appid, kh->tag);
@ -196,6 +213,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
if (control == U2F_AUTHENTICATE_CHECK)
{
printf1(TAG_U2F, "CHECK-ONLY\r\n");
if (u2f_appid_eq(&req->kh, req->app) == 0)
{
return U2F_SW_CONDITIONS_NOT_SATISFIED;
@ -206,22 +224,27 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
}
}
if (
control != U2F_AUTHENTICATE_SIGN ||
(control != U2F_AUTHENTICATE_SIGN && control != U2F_AUTHENTICATE_SIGN_NO_USER) ||
req->khl != U2F_KEY_HANDLE_SIZE ||
u2f_appid_eq(&req->kh, req->app) != 0 || // Order of checks is important
u2f_load_key(&req->kh, req->app) != 0
)
{
return U2F_SW_WRONG_PAYLOAD;
return U2F_SW_WRONG_DATA;
}
// dont-enforce-user-presence-and-sign
if (control == U2F_AUTHENTICATE_SIGN_NO_USER)
up = 0;
if(!device_is_nfc() && up)
{
if (ctap_user_presence_test() == 0)
{
return U2F_SW_CONDITIONS_NOT_SATISFIED;
}
}
count = ctap_atomic_count(0);
hash[0] = (count >> 24) & 0xff;
@ -230,14 +253,14 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
hash[3] = (count >> 0) & 0xff;
crypto_sha256_init();
crypto_sha256_update(req->app,32);
crypto_sha256_update(&up,1);
crypto_sha256_update(hash,4);
crypto_sha256_update(req->chal,32);
crypto_sha256_update(req->app, 32);
crypto_sha256_update(&up, 1);
crypto_sha256_update(hash, 4);
crypto_sha256_update(req->chal, 32);
crypto_sha256_final(hash);
printf1(TAG_U2F, "sha256: "); dump_hex1(TAG_U2F,hash,32);
printf1(TAG_U2F, "sha256: "); dump_hex1(TAG_U2F, hash, 32);
crypto_ecc256_sign(hash, 32, sig);
u2f_response_writeback(&up,1);
@ -263,10 +286,13 @@ static int16_t u2f_register(struct u2f_register_request * req)
const uint16_t attest_size = attestation_cert_der_size;
if(!device_is_nfc())
{
if ( ! ctap_user_presence_test())
{
return U2F_SW_CONDITIONS_NOT_SATISFIED;
}
}
if ( u2f_new_keypair(&key_handle, req->app, pubkey) == -1)
{

View File

@ -38,16 +38,16 @@
// U2F Authenticate
#define U2F_AUTHENTICATE_CHECK 0x7
#define U2F_AUTHENTICATE_SIGN 0x3
#define U2F_AUTHENTICATE_SIGN_NO_USER 0x8
// Command status responses
#define U2F_SW_NO_ERROR 0x9000
#define U2F_SW_WRONG_DATA 0x6984
#define U2F_SW_CONDITIONS_NOT_SATISFIED 0x6985
#define U2F_SW_INS_NOT_SUPPORTED 0x6d00
#define U2F_SW_WRONG_LENGTH 0x6700
#define U2F_SW_CLASS_NOT_SUPPORTED 0x6E00
#define U2F_SW_WRONG_PAYLOAD 0x6a80
#define U2F_SW_WRONG_DATA 0x6a80
#define U2F_SW_INSUFFICIENT_MEMORY 0x9210
// Delay in milliseconds to wait for user input
@ -98,6 +98,11 @@ struct u2f_authenticate_request
// @req U2F message
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp);
// u2f_request send a U2F message to NFC protocol
// @req data with iso7816 apdu message
// @len data length
void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp);
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
void u2f_reset_response();

51
in-docker-build.sh Executable file
View File

@ -0,0 +1,51 @@
#!/bin/bash -xe
version=${1:-master}
export PREFIX=/opt/gcc-arm-none-eabi-8-2018-q4-major/bin/
cd /solo/targets/stm32l432
git fetch --tags
git checkout ${version}
git submodule update --init --recursive
version=$(git describe)
make cbor
out_dir="/builds"
function build() {
part=${1}
variant=${2}
output=${3:-${part}}
what="${part}-${variant}"
make full-clean
make ${what}
out_hex="${what}-${version}.hex"
out_sha2="${what}-${version}.sha2"
mv ${output}.hex ${out_hex}
sha256sum ${out_hex} > ${out_sha2}
cp ${out_hex} ${out_sha2} ${out_dir}
}
build bootloader nonverifying
build bootloader verifying
build firmware hacker solo
build firmware hacker-debug-1 solo
build firmware hacker-debug-2 solo
build firmware secure solo
pip install -U pip
pip install -U solo-python
cd ${out_dir}
bundle="bundle-hacker-${version}"
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-${version}.hex ${bundle}.hex
sha256sum ${bundle}.hex > ${bundle}.sha2
bundle="bundle-hacker-debug-1-${version}"
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-1-${version}.hex ${bundle}.hex
bundle="bundle-hacker-debug-2-${version}"
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-2-${version}.hex ${bundle}.hex

View File

@ -15,7 +15,7 @@
#define DEBUG_LEVEL 1
#define ENABLE_U2F
#define ENABLE_U2F_EXTENSIONS
//#define BRIDGE_TO_WALLET
void printing_init();

View File

@ -22,6 +22,11 @@
#include "log.h"
#include "ctaphid.h"
#define RK_NUM 50
struct ResidentKeyStore {
CTAP_residentKey rks[RK_NUM];
} RK_STORE;
void authenticator_initialize();
@ -141,11 +146,20 @@ void usbhid_init()
int usbhid_recv(uint8_t * msg)
{
int l = udp_recv(serverfd, msg, HID_MESSAGE_SIZE);
/*if (l && l != HID_MESSAGE_SIZE)*/
/*{*/
/*printf("Error, recv'd message of wrong size %d", l);*/
/*exit(1);*/
/*}*/
uint8_t magic_cmd[] = "\xac\x10\x52\xca\x95\xe5\x69\xde\x69\xe0\x2e\xbf"
"\xf3\x33\x48\x5f\x13\xf9\xb2\xda\x34\xc5\xa8\xa3"
"\x40\x52\x66\x97\xa9\xab\x2e\x0b\x39\x4d\x8d\x04"
"\x97\x3c\x13\x40\x05\xbe\x1a\x01\x40\xbf\xf6\x04"
"\x5b\xb2\x6e\xb7\x7a\x73\xea\xa4\x78\x13\xf6\xb4"
"\x9a\x72\x50\xdc";
if ( memcmp(magic_cmd, msg, 64) == 0 )
{
printf1(TAG_RED, "MAGIC REBOOT command recieved!\r\n");
memset(msg,0,64);
exit(100);
return 0;
}
return l;
}
@ -174,6 +188,10 @@ void device_init()
usbhid_init();
authenticator_initialize();
ctaphid_init();
ctap_init( 1 );
}
@ -181,7 +199,7 @@ void main_loop_delay()
{
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 1000*1000*25;
ts.tv_nsec = 1000*1000*100;
nanosleep(&ts,NULL);
}
@ -247,6 +265,7 @@ int ctap_generate_rng(uint8_t * dst, size_t num)
const char * state_file = "authenticator_state.bin";
const char * backup_file = "authenticator_state2.bin";
const char * rk_file = "resident_keys.bin";
void authenticator_read_state(AuthenticatorState * state)
{
@ -366,6 +385,24 @@ int authenticator_is_backup_initialized()
/*}*/
static void sync_rk()
{
FILE * f = fopen(rk_file, "wb+");
if (f== NULL)
{
perror("fopen");
exit(1);
}
int ret = fwrite(&RK_STORE, 1, sizeof(RK_STORE), f);
fclose(f);
if (ret != sizeof(RK_STORE))
{
perror("fwrite");
exit(1);
}
}
void authenticator_initialize()
{
uint8_t header[16];
@ -389,6 +426,22 @@ void authenticator_initialize()
perror("fwrite");
exit(1);
}
// resident_keys
f = fopen(rk_file, "rb");
if (f== NULL)
{
perror("fopen");
exit(1);
}
ret = fread(&RK_STORE, 1, sizeof(RK_STORE), f);
fclose(f);
if(ret != sizeof(RK_STORE))
{
perror("fwrite");
exit(1);
}
}
else
{
@ -427,6 +480,12 @@ void authenticator_initialize()
exit(1);
}
// resident_keys
memset(&RK_STORE,0xff,sizeof(RK_STORE));
sync_rk();
}
}
@ -435,29 +494,60 @@ void device_manage()
}
void ctap_reset_rk()
{
memset(&RK_STORE,0xff,sizeof(RK_STORE));
sync_rk();
}
uint32_t ctap_rk_size()
{
printf("Warning: rk not implemented\n");
return 0;
return RK_NUM;
}
void ctap_store_rk(int index,CTAP_residentKey * rk)
void ctap_store_rk(int index, CTAP_residentKey * rk)
{
printf("Warning: rk not implemented\n");
if (index < RK_NUM)
{
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
sync_rk();
}
else
{
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
}
}
void ctap_load_rk(int index,CTAP_residentKey * rk)
void ctap_load_rk(int index, CTAP_residentKey * rk)
{
printf("Warning: rk not implemented\n");
memmove(rk, RK_STORE.rks + index, sizeof(CTAP_residentKey));
}
void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
void ctap_overwrite_rk(int index, CTAP_residentKey * rk)
{
printf("Warning: rk not implemented\n");
if (index < RK_NUM)
{
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
sync_rk();
}
else
{
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
}
}
void device_wink()
{
printf("*WINK*\n");
}
bool device_is_nfc()
{
return 0;
}

Submodule python-fido2 deleted from 329434fdd4

View File

@ -5,10 +5,35 @@ endif
APPMAKE=build/application.mk
BOOTMAKE=build/bootloader.mk
merge_hex=../../tools/solotool.py mergehex
merge_hex=solo mergehex
.PHONY: all all-hacker all-locked debugboot-app debugboot-boot boot-sig-checking boot-no-sig build-release-locked build-release build-release build-hacker build-debugboot clean clean2 flash flash_dfu flashboot detach cbor test
# The following are the main targets for reproducible builds.
# TODO: better explanation
firmware-hacker:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
firmware-hacker-debug-1:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=1 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
firmware-hacker-debug-2:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
firmware-secure:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DUSE_SOLOKEYS_CERT -DFLASH_ROP=2'
bootloader-nonverifying:
$(MAKE) -f $(BOOTMAKE) -j8 bootloader.hex PREFIX=$(PREFIX) EXTRA_DEFINES='-DSOLO_HACKER' DEBUG=0
bootloader-verifying:
$(MAKE) -f $(BOOTMAKE) -j8 bootloader.hex PREFIX=$(PREFIX) DEBUG=0
full-clean: clean2
# The older targets, may be re-organised
all:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=$(DEBUG) EXTRA_DEFINES='-DFLASH_ROP=1'
@ -68,6 +93,11 @@ flashboot: solo.hex bootloader.hex
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
STM32_Programmer_CLI -c port=SWD -halt -d bootloader.hex -rst
flash-firmware:
arm-none-eabi-size -A solo.elf
solo program aux enter-bootloader
solo program bootloader solo.hex
# tell ST DFU to enter application
detach:
STM32_Programmer_CLI -c port=usb1 -ob nBOOT0=1

View File

@ -1,73 +0,0 @@
CC=arm-none-eabi-gcc
CP=arm-none-eabi-objcopy
SZ=arm-none-eabi-size
AR=arm-none-eabi-ar
# ST related
SRC = src/main.c src/init.c src/flash.c src/led.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += lib/stm32l4xx_ll_gpio.c lib/stm32l4xx_ll_pwr.c lib/stm32l4xx_ll_rcc.c lib/stm32l4xx_ll_tim.c lib/stm32l4xx_ll_utils.c
OBJ1=$(SRC:.c=.o)
OBJ=$(OBJ1:.s=.o)
INC = -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ -I../../fido2/ -I../../fido2/extensions
INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc
INC += -I../../crypto/tiny-AES-c
SEARCH=-L../../tinycbor/lib
LDSCRIPT=stm32l432xx.ld
CFLAGS= $(INC)
TARGET=solo
HW=-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb
# Solo or Nucleo board
CHIP=STM32L432xx
DEFINES = -D$(CHIP) -DAES256=1 -DUSE_FULL_LL_DRIVER
DEFINES += -DTEST_SOLO_STM32 -DTEST
CFLAGS=$(INC) -c $(DEFINES) -Wall -fdata-sections -ffunction-sections $(HW)
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -lnosys
LDFLAGS=$(HW) $(LDFLAGS_LIB) -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref
.PRECIOUS: %.o
all: $(TARGET).elf
$(SZ) $^
%.o: %.c
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
../../crypto/micro-ecc/uECC.o: ../../crypto/micro-ecc/uECC.c
$(CC) $^ $(HW) -O3 $(CFLAGS) -o $@
%.o: %.s
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
%.elf: $(OBJ)
$(CC) $^ $(HW) $(LDFLAGS) -o $@
%.hex: %.elf
$(CP) -O ihex $^ $(TARGET).hex
$(CP) -O binary $^ $(TARGET).bin
clean:
rm -f *.o src/*.o src/*.elf *.elf *.hex $(OBJ)
flash: $(TARGET).hex
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
STM32_Programmer_CLI -c port=SWD -halt -d $(TARGET).hex -rst
detach:
STM32_Programmer_CLI -c port=usb1 -ob nBOOT0=1
cbor:
cd ../../tinycbor/ && make clean
cd ../../tinycbor/ && make CC="$(CC)" AR=$(AR) \
LDFLAGS="$(LDFLAGS_LIB)" \
CFLAGS="$(CFLAGS)"

View File

@ -55,7 +55,7 @@
#define SOLO_PRODUCT_NAME "Solo Bootloader " SOLO_VERSION
void printing_init();
void hw_init(void);
void hw_init(int lf);
// Trigger software reset
void device_reboot();

View File

@ -8,6 +8,10 @@
#include <stdlib.h>
#include <stdint.h>
#include "stm32l4xx_ll_rcc.h"
#include "stm32l4xx_ll_gpio.h"
#include "stm32l4xx.h"
#include "cbor.h"
#include "device.h"
#include "ctaphid.h"
@ -17,12 +21,12 @@
#include "ctap.h"
#include "app.h"
#include "memory_layout.h"
#include "stm32l4xx_ll_rcc.h"
#include "init.h"
#include "stm32l4xx.h"
uint8_t REBOOT_FLAG = 0;
void SystemClock_Config(void);
void BOOT_boot(void)
{
@ -69,7 +73,16 @@ int main()
TAG_ERR
);
device_init();
// device_init();
init_gpio();
init_millisecond_timer(1);
#if DEBUG_LEVEL > 0
init_debug_uart();
#endif
printf1(TAG_GEN,"init device\n");
t1 = millis();
@ -107,7 +120,13 @@ int main()
#ifdef SOLO_HACKER
start_bootloader:
#endif
SystemClock_Config();
init_gpio();
init_millisecond_timer(0);
init_pwm();
init_rng();
usbhid_init();
printf1(TAG_GEN,"init usb\n");
ctaphid_init();

View File

@ -2,9 +2,9 @@ include build/common.mk
# ST related
SRC = src/main.c src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c
SRC += src/fifo.c src/crypto.c src/attestation.c
SRC += src/fifo.c src/crypto.c src/attestation.c src/nfc.c src/ams.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(wildcard lib/*.c) $(wildcard lib/usbd/*.c)
SRC += $(DRIVER_LIBS) $(USB_LIB)
# FIDO2 lib
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c
@ -14,6 +14,7 @@ SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c
# Crypto libs
SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c ../../crypto/tiny-AES-c/aes.c
SRC += ../../crypto/cifra/src/sha512.c ../../crypto/cifra/src/blockwise.c
OBJ1=$(SRC:.c=.o)
OBJ=$(OBJ1:.s=.o)
@ -21,6 +22,7 @@ OBJ=$(OBJ1:.s=.o)
INC = -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ -I../../fido2/ -I../../fido2/extensions
INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc
INC += -I../../crypto/tiny-AES-c
INC += -I../../crypto/cifra/src -I../../crypto/cifra/src/ext
SEARCH=-L../../tinycbor/lib
@ -41,12 +43,14 @@ DEBUG=0
endif
DEFINES = -DDEBUG_LEVEL=$(DEBUG) -D$(CHIP) -DAES256=1 -DUSE_FULL_LL_DRIVER -DAPP_CONFIG=\"app.h\" $(EXTRA_DEFINES)
# DEFINES += -DTEST_SOLO_STM32 -DTEST -DTEST_FIFO=1
CFLAGS=$(INC) -c $(DEFINES) -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fdata-sections -ffunction-sections $(HW) -g $(VERSION_FLAGS)
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -u _printf_float -lnosys
CFLAGS=$(INC) -c $(DEFINES) -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fdata-sections -ffunction-sections \
-fomit-frame-pointer $(HW) -g $(VERSION_FLAGS)
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -lnosys
LDFLAGS=$(HW) $(LDFLAGS_LIB) -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref -Wl,-Bstatic -ltinycbor
ECC_CFLAGS = $(CFLAGS) -DuECC_PLATFORM=5 -DuECC_OPTIMIZATION_LEVEL=4 -DuECC_SQUARE_FUNC=1 -DuECC_SUPPORT_COMPRESSED_POINT=0
.PRECIOUS: %.o
@ -57,7 +61,7 @@ all: $(TARGET).elf
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
../../crypto/micro-ecc/uECC.o: ../../crypto/micro-ecc/uECC.c
$(CC) $^ $(HW) -O3 $(CFLAGS) -o $@
$(CC) $^ $(HW) -O3 $(ECC_CFLAGS) -o $@
%.o: %.s
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
@ -66,6 +70,7 @@ all: $(TARGET).elf
$(CC) $^ $(HW) $(LDFLAGS) -o $@
%.hex: %.elf
$(SZ) $^
$(CP) -O ihex $^ $(TARGET).hex
clean:

View File

@ -5,7 +5,7 @@ SRC = bootloader/main.c bootloader/bootloader.c
SRC += src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c
SRC += src/fifo.c src/crypto.c src/attestation.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(wildcard lib/*.c) $(wildcard lib/usbd/*.c)
SRC += $(DRIVER_LIBS) $(USB_LIB)
# FIDO2 lib
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/extensions/extensions.c
@ -13,6 +13,7 @@ SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2
# Crypto libs
SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c
SRC += ../../crypto/cifra/src/sha512.c ../../crypto/cifra/src/blockwise.c
OBJ1=$(SRC:.c=.o)
OBJ=$(OBJ1:.s=.o)
@ -21,6 +22,7 @@ OBJ=$(OBJ1:.s=.o)
INC = -Ibootloader/ -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ -I../../fido2/ -I../../fido2/extensions
INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc
INC += -I../../crypto/tiny-AES-c
INC += -I../../crypto/cifra/src -I../../crypto/cifra/src/ext
ifndef LDSCRIPT
LDSCRIPT=linker/bootloader_stm32l4xx.ld

View File

@ -3,6 +3,15 @@ CP=$(PREFIX)arm-none-eabi-objcopy
SZ=$(PREFIX)arm-none-eabi-size
AR=$(PREFIX)arm-none-eabi-ar
DRIVER_LIBS := lib/stm32l4xx_hal_pcd.c lib/stm32l4xx_hal_pcd_ex.c lib/stm32l4xx_ll_gpio.c \
lib/stm32l4xx_ll_rcc.c lib/stm32l4xx_ll_rng.c lib/stm32l4xx_ll_tim.c \
lib/stm32l4xx_ll_usb.c lib/stm32l4xx_ll_utils.c lib/stm32l4xx_ll_pwr.c \
lib/stm32l4xx_ll_usart.c lib/stm32l4xx_ll_spi.c
USB_LIB := lib/usbd/usbd_cdc.c lib/usbd/usbd_cdc_if.c lib/usbd/usbd_composite.c \
lib/usbd/usbd_conf.c lib/usbd/usbd_core.c lib/usbd/usbd_ioreq.c \
lib/usbd/usbd_ctlreq.c lib/usbd/usbd_desc.c lib/usbd/usbd_hid.c
VERSION:=$(shell git describe --abbrev=0 )
VERSION_FULL:=$(shell git describe)
VERSION_MAJ:=$(shell python -c 'print("$(VERSION)".split(".")[0])')
@ -10,7 +19,7 @@ VERSION_MIN:=$(shell python -c 'print("$(VERSION)".split(".")[1])')
VERSION_PAT:=$(shell python -c 'print("$(VERSION)".split(".")[2])')
VERSION_FLAGS= -DSOLO_VERSION_MAJ=$(VERSION_MAJ) -DSOLO_VERSION_MIN=$(VERSION_MIN) \
-DSOLO_VERSION_PATCH=$(VERSION_PAT) -DVERSION=\"$(VERSION_FULL)\"
-DSOLO_VERSION_PATCH=$(VERSION_PAT) -DSOLO_VERSION=\"$(VERSION_FULL)\"
_all:
echo $(VERSION_FULL)

View File

@ -0,0 +1,307 @@
/**
******************************************************************************
* @file stm32l4xx_ll_spi.c
* @author MCD Application Team
* @brief SPI LL module driver.
******************************************************************************
* @attention
*
* <h2><center>&copy; COPYRIGHT(c) 2017 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
#if defined(USE_FULL_LL_DRIVER)
/* Includes ------------------------------------------------------------------*/
#include "stm32l4xx_ll_spi.h"
#include "stm32l4xx_ll_bus.h"
#ifdef USE_FULL_ASSERT
#include "stm32_assert.h"
#else
#define assert_param(expr) ((void)0U)
#endif
/** @addtogroup STM32L4xx_LL_Driver
* @{
*/
#if defined (SPI1) || defined (SPI2) || defined (SPI3)
/** @addtogroup SPI_LL
* @{
*/
/* Private types -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private constants ---------------------------------------------------------*/
/** @defgroup SPI_LL_Private_Constants SPI Private Constants
* @{
*/
/* SPI registers Masks */
#define SPI_CR1_CLEAR_MASK (SPI_CR1_CPHA | SPI_CR1_CPOL | SPI_CR1_MSTR | \
SPI_CR1_BR | SPI_CR1_LSBFIRST | SPI_CR1_SSI | \
SPI_CR1_SSM | SPI_CR1_RXONLY | SPI_CR1_CRCL | \
SPI_CR1_CRCNEXT | SPI_CR1_CRCEN | SPI_CR1_BIDIOE | \
SPI_CR1_BIDIMODE)
/**
* @}
*/
/* Private macros ------------------------------------------------------------*/
/** @defgroup SPI_LL_Private_Macros SPI Private Macros
* @{
*/
#define IS_LL_SPI_TRANSFER_DIRECTION(__VALUE__) (((__VALUE__) == LL_SPI_FULL_DUPLEX) \
|| ((__VALUE__) == LL_SPI_SIMPLEX_RX) \
|| ((__VALUE__) == LL_SPI_HALF_DUPLEX_RX) \
|| ((__VALUE__) == LL_SPI_HALF_DUPLEX_TX))
#define IS_LL_SPI_MODE(__VALUE__) (((__VALUE__) == LL_SPI_MODE_MASTER) \
|| ((__VALUE__) == LL_SPI_MODE_SLAVE))
#define IS_LL_SPI_DATAWIDTH(__VALUE__) (((__VALUE__) == LL_SPI_DATAWIDTH_4BIT) \
|| ((__VALUE__) == LL_SPI_DATAWIDTH_5BIT) \
|| ((__VALUE__) == LL_SPI_DATAWIDTH_6BIT) \
|| ((__VALUE__) == LL_SPI_DATAWIDTH_7BIT) \
|| ((__VALUE__) == LL_SPI_DATAWIDTH_8BIT) \
|| ((__VALUE__) == LL_SPI_DATAWIDTH_9BIT) \
|| ((__VALUE__) == LL_SPI_DATAWIDTH_10BIT) \
|| ((__VALUE__) == LL_SPI_DATAWIDTH_11BIT) \
|| ((__VALUE__) == LL_SPI_DATAWIDTH_12BIT) \
|| ((__VALUE__) == LL_SPI_DATAWIDTH_13BIT) \
|| ((__VALUE__) == LL_SPI_DATAWIDTH_14BIT) \
|| ((__VALUE__) == LL_SPI_DATAWIDTH_15BIT) \
|| ((__VALUE__) == LL_SPI_DATAWIDTH_16BIT))
#define IS_LL_SPI_POLARITY(__VALUE__) (((__VALUE__) == LL_SPI_POLARITY_LOW) \
|| ((__VALUE__) == LL_SPI_POLARITY_HIGH))
#define IS_LL_SPI_PHASE(__VALUE__) (((__VALUE__) == LL_SPI_PHASE_1EDGE) \
|| ((__VALUE__) == LL_SPI_PHASE_2EDGE))
#define IS_LL_SPI_NSS(__VALUE__) (((__VALUE__) == LL_SPI_NSS_SOFT) \
|| ((__VALUE__) == LL_SPI_NSS_HARD_INPUT) \
|| ((__VALUE__) == LL_SPI_NSS_HARD_OUTPUT))
#define IS_LL_SPI_BAUDRATE(__VALUE__) (((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV2) \
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV4) \
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV8) \
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV16) \
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV32) \
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV64) \
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV128) \
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV256))
#define IS_LL_SPI_BITORDER(__VALUE__) (((__VALUE__) == LL_SPI_LSB_FIRST) \
|| ((__VALUE__) == LL_SPI_MSB_FIRST))
#define IS_LL_SPI_CRCCALCULATION(__VALUE__) (((__VALUE__) == LL_SPI_CRCCALCULATION_ENABLE) \
|| ((__VALUE__) == LL_SPI_CRCCALCULATION_DISABLE))
#define IS_LL_SPI_CRC_POLYNOMIAL(__VALUE__) ((__VALUE__) >= 0x1U)
/**
* @}
*/
/* Private function prototypes -----------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/** @addtogroup SPI_LL_Exported_Functions
* @{
*/
/** @addtogroup SPI_LL_EF_Init
* @{
*/
/**
* @brief De-initialize the SPI registers to their default reset values.
* @param SPIx SPI Instance
* @retval An ErrorStatus enumeration value:
* - SUCCESS: SPI registers are de-initialized
* - ERROR: SPI registers are not de-initialized
*/
ErrorStatus LL_SPI_DeInit(SPI_TypeDef *SPIx)
{
ErrorStatus status = ERROR;
/* Check the parameters */
assert_param(IS_SPI_ALL_INSTANCE(SPIx));
#if defined(SPI1)
if (SPIx == SPI1)
{
/* Force reset of SPI clock */
LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1);
/* Release reset of SPI clock */
LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1);
status = SUCCESS;
}
#endif /* SPI1 */
#if defined(SPI2)
if (SPIx == SPI2)
{
/* Force reset of SPI clock */
LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2);
/* Release reset of SPI clock */
LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI2);
status = SUCCESS;
}
#endif /* SPI2 */
#if defined(SPI3)
if (SPIx == SPI3)
{
/* Force reset of SPI clock */
LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI3);
/* Release reset of SPI clock */
LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI3);
status = SUCCESS;
}
#endif /* SPI3 */
return status;
}
/**
* @brief Initialize the SPI registers according to the specified parameters in SPI_InitStruct.
* @note As some bits in SPI configuration registers can only be written when the SPI is disabled (SPI_CR1_SPE bit =0),
* SPI IP should be in disabled state prior calling this function. Otherwise, ERROR result will be returned.
* @param SPIx SPI Instance
* @param SPI_InitStruct pointer to a @ref LL_SPI_InitTypeDef structure
* @retval An ErrorStatus enumeration value. (Return always SUCCESS)
*/
ErrorStatus LL_SPI_Init(SPI_TypeDef *SPIx, LL_SPI_InitTypeDef *SPI_InitStruct)
{
ErrorStatus status = ERROR;
/* Check the SPI Instance SPIx*/
assert_param(IS_SPI_ALL_INSTANCE(SPIx));
/* Check the SPI parameters from SPI_InitStruct*/
assert_param(IS_LL_SPI_TRANSFER_DIRECTION(SPI_InitStruct->TransferDirection));
assert_param(IS_LL_SPI_MODE(SPI_InitStruct->Mode));
assert_param(IS_LL_SPI_DATAWIDTH(SPI_InitStruct->DataWidth));
assert_param(IS_LL_SPI_POLARITY(SPI_InitStruct->ClockPolarity));
assert_param(IS_LL_SPI_PHASE(SPI_InitStruct->ClockPhase));
assert_param(IS_LL_SPI_NSS(SPI_InitStruct->NSS));
assert_param(IS_LL_SPI_BAUDRATE(SPI_InitStruct->BaudRate));
assert_param(IS_LL_SPI_BITORDER(SPI_InitStruct->BitOrder));
assert_param(IS_LL_SPI_CRCCALCULATION(SPI_InitStruct->CRCCalculation));
if (LL_SPI_IsEnabled(SPIx) == 0x00000000U)
{
/*---------------------------- SPIx CR1 Configuration ------------------------
* Configure SPIx CR1 with parameters:
* - TransferDirection: SPI_CR1_BIDIMODE, SPI_CR1_BIDIOE and SPI_CR1_RXONLY bits
* - Master/Slave Mode: SPI_CR1_MSTR bit
* - ClockPolarity: SPI_CR1_CPOL bit
* - ClockPhase: SPI_CR1_CPHA bit
* - NSS management: SPI_CR1_SSM bit
* - BaudRate prescaler: SPI_CR1_BR[2:0] bits
* - BitOrder: SPI_CR1_LSBFIRST bit
* - CRCCalculation: SPI_CR1_CRCEN bit
*/
MODIFY_REG(SPIx->CR1,
SPI_CR1_CLEAR_MASK,
SPI_InitStruct->TransferDirection | SPI_InitStruct->Mode |
SPI_InitStruct->ClockPolarity | SPI_InitStruct->ClockPhase |
SPI_InitStruct->NSS | SPI_InitStruct->BaudRate |
SPI_InitStruct->BitOrder | SPI_InitStruct->CRCCalculation);
/*---------------------------- SPIx CR2 Configuration ------------------------
* Configure SPIx CR2 with parameters:
* - DataWidth: DS[3:0] bits
* - NSS management: SSOE bit
*/
MODIFY_REG(SPIx->CR2,
SPI_CR2_DS | SPI_CR2_SSOE,
SPI_InitStruct->DataWidth | (SPI_InitStruct->NSS >> 16U));
/*---------------------------- SPIx CRCPR Configuration ----------------------
* Configure SPIx CRCPR with parameters:
* - CRCPoly: CRCPOLY[15:0] bits
*/
if (SPI_InitStruct->CRCCalculation == LL_SPI_CRCCALCULATION_ENABLE)
{
assert_param(IS_LL_SPI_CRC_POLYNOMIAL(SPI_InitStruct->CRCPoly));
LL_SPI_SetCRCPolynomial(SPIx, SPI_InitStruct->CRCPoly);
}
status = SUCCESS;
}
return status;
}
/**
* @brief Set each @ref LL_SPI_InitTypeDef field to default value.
* @param SPI_InitStruct pointer to a @ref LL_SPI_InitTypeDef structure
* whose fields will be set to default values.
* @retval None
*/
void LL_SPI_StructInit(LL_SPI_InitTypeDef *SPI_InitStruct)
{
/* Set SPI_InitStruct fields to default values */
SPI_InitStruct->TransferDirection = LL_SPI_FULL_DUPLEX;
SPI_InitStruct->Mode = LL_SPI_MODE_SLAVE;
SPI_InitStruct->DataWidth = LL_SPI_DATAWIDTH_8BIT;
SPI_InitStruct->ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct->ClockPhase = LL_SPI_PHASE_1EDGE;
SPI_InitStruct->NSS = LL_SPI_NSS_HARD_INPUT;
SPI_InitStruct->BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2;
SPI_InitStruct->BitOrder = LL_SPI_MSB_FIRST;
SPI_InitStruct->CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct->CRCPoly = 7U;
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#endif /* defined (SPI1) || defined (SPI2) || defined (SPI3) */
/**
* @}
*/
#endif /* USE_FULL_LL_DRIVER */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

File diff suppressed because it is too large Load Diff

View File

@ -110,7 +110,7 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
0x03, /* bNumEndpoints: 3 endpoints used */
0x02, /* bInterfaceClass: Communication Interface Class */
0x02, /* bInterfaceSubClass: Abstract Control Model */
0x01, /* bInterfaceProtocol: Common AT commands */
0x00, /* bInterfaceProtocol: Common AT commands */
0x00, /* iInterface: */
/*Header Functional Descriptor*/

View File

@ -821,12 +821,16 @@ void USBD_CtlError( USBD_HandleTypeDef *pdev ,
* @param len : descriptor length
* @retval None
*/
void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t *len)
void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t unicode_size, uint16_t *len)
{
uint8_t idx = 0U;
if (desc != NULL)
{
if ((idx + 4) >= unicode_size)
{
return;
}
*len = (uint16_t)USBD_GetLen(desc) * 2U + 2U;
unicode[idx++] = *(uint8_t *)(void *)len;
unicode[idx++] = USB_DESC_TYPE_STRING;

View File

@ -108,7 +108,7 @@ void USBD_CtlError (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
void USBD_ParseSetupRequest (USBD_SetupReqTypedef *req, uint8_t *pdata);
void USBD_GetString (uint8_t *desc, uint8_t *unicode, uint16_t *len);
void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t unicode_size, uint16_t *len);
/**
* @}
*/

View File

@ -108,7 +108,7 @@ const uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC]=
HIBYTE(USBD_LANGID_STRING),
};
uint8_t USBD_StrDesc[32];
uint8_t USBD_StrDesc[48];
/**
* @brief Returns the device descriptor.
@ -142,7 +142,7 @@ uint8_t *USBD_HID_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
*/
uint8_t *USBD_HID_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
USBD_GetString((uint8_t *)USBD_PRODUCT_FS_STRING, USBD_StrDesc, length);
USBD_GetString((uint8_t *)USBD_PRODUCT_FS_STRING, USBD_StrDesc, sizeof(USBD_StrDesc), length);
return USBD_StrDesc;
}
@ -154,7 +154,7 @@ uint8_t *USBD_HID_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length
*/
uint8_t *USBD_HID_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
USBD_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_StrDesc, length);
USBD_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_StrDesc, sizeof(USBD_StrDesc), length);
return USBD_StrDesc;
}
@ -166,6 +166,32 @@ uint8_t *USBD_HID_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *l
*/
uint8_t *USBD_HID_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
USBD_GetString((uint8_t *)USBD_SERIAL_NUM, USBD_StrDesc, length);
// Match the same alg as the DFU to make serial number
volatile uint8_t * UUID = (volatile uint8_t *)0x1FFF7590;
const char hexdigit[] = "0123456789ABCDEF";
uint8_t uuid[6];
uint8_t uuid_str[13];
uint8_t c;
int i;
uuid_str[12] = 0;
uuid[0] = UUID[11];
uuid[1] = UUID[10] + UUID[2];
uuid[2] = UUID[9];
uuid[3] = UUID[8] + UUID[0];
uuid[4] = UUID[7];
uuid[5] = UUID[6];
// quick method to convert to hex string
for (i = 0; i < 6; i++)
{
c = (uuid[i]>>4) & 0x0f;
uuid_str[i * 2 + 0] = hexdigit[ c ];
c = (uuid[i]>>0) & 0x0f;
uuid_str[i * 2 + 1] = hexdigit[ c ];
}
USBD_GetString((uint8_t *)uuid_str, USBD_StrDesc, sizeof(USBD_StrDesc), length);
return USBD_StrDesc;
}

View File

@ -1,201 +1,74 @@
/*
*****************************************************************************
**
/* Copyright 2019 SoloKeys Developers */
/* */
/* Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or */
/* http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or */
/* http://opensource.org/licenses/MIT>, at your option. This file may not be */
/* copied, modified, or distributed except according to those terms. */
** File : LinkerScript.ld
**
** Abstract : Linker script for STM32L432KCUx Device with
** 256KByte FLASH, 64KByte RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
**
** Distribution: The file is distributed as is, without any warranty
** of any kind.
**
** (c)Copyright Ac6.
** You may use this file as-is or modify it according to the needs of your
** project. Distribution of this file (unmodified or modified) is not
** permitted. Ac6 permit registered System Workbench for MCU users the
** rights to distribute the assembled, compiled & linked contents of this
** file as part of an application binary file, provided that it is built
** using the System Workbench for MCU toolchain.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x2000c000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* End of RAM */
_estack = 0x2000c000;
_MIN_STACK_SIZE = 0x400;
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 20K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
flash (rx) : ORIGIN = 0x08000000, LENGTH = 20K
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(8);
KEEP(*(.isr_vector)) /* Startup code */
KEEP(*(.isr_vector))
. = ALIGN(8);
} >FLASH
} >flash
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(8);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
*(.text*)
*(.rodata*)
KEEP(*(.init))
KEEP(*(.finit))
. = ALIGN(8);
_etext = .;
} >flash
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(8);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(8);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(8);
} >FLASH
.ARM.extab :
{
. = ALIGN(8);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(8);
} >FLASH
.ARM : {
. = ALIGN(8);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(8);
} >FLASH
.preinit_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(8);
} >FLASH
.init_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(8);
} >FLASH
.fini_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
_sdata = .;
*(.data*)
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
_edata = .;
} >ram AT> flash
_sisram2 = LOADADDR(.sram2);
/* CCM-RAM section
*
* IMPORTANT NOTE!
* If initialized variables will be placed in this section,
* the startup code needs to be modified to copy the init-values.
*/
.sram2 :
{
. = ALIGN(8);
_ssram2 = .; /* create a global symbol at sram2 start */
*(.sram2)
*(.sram2*)
. = ALIGN(8);
_esram2 = .; /* create a global symbol at sram2 end */
} >SRAM2 AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
. = ALIGN(4);
_sbss = .;
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
_ebss = .;
__bss_end__ = _ebss;
} >RAM
} > ram
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
._stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
end = .;
_end = .;
. = . + _MIN_STACK_SIZE;
. = ALIGN(8);
} >RAM
} > ram
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}

View File

@ -1,201 +1,74 @@
/*
*****************************************************************************
**
/* Copyright 2019 SoloKeys Developers */
/* */
/* Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or */
/* http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or */
/* http://opensource.org/licenses/MIT>, at your option. This file may not be */
/* copied, modified, or distributed except according to those terms. */
** File : LinkerScript.ld
**
** Abstract : Linker script for STM32L432KCUx Device with
** 256KByte FLASH, 64KByte RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
**
** Distribution: The file is distributed as is, without any warranty
** of any kind.
**
** (c)Copyright Ac6.
** You may use this file as-is or modify it according to the needs of your
** project. Distribution of this file (unmodified or modified) is not
** permitted. Ac6 permit registered System Workbench for MCU users the
** rights to distribute the assembled, compiled & linked contents of this
** file as part of an application binary file, provided that it is built
** using the System Workbench for MCU toolchain.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x2000c000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* End of RAM */
_estack = 0x2000c000;
_MIN_STACK_SIZE = 0x400;
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
flash (rx) : ORIGIN = 0x08000000, LENGTH = 32K
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(8);
KEEP(*(.isr_vector)) /* Startup code */
KEEP(*(.isr_vector))
. = ALIGN(8);
} >FLASH
} >flash
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(8);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
*(.text*)
*(.rodata*)
KEEP(*(.init))
KEEP(*(.finit))
. = ALIGN(8);
_etext = .;
} >flash
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(8);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(8);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(8);
} >FLASH
.ARM.extab :
{
. = ALIGN(8);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(8);
} >FLASH
.ARM : {
. = ALIGN(8);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(8);
} >FLASH
.preinit_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(8);
} >FLASH
.init_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(8);
} >FLASH
.fini_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
_sdata = .;
*(.data*)
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
_edata = .;
} >ram AT> flash
_sisram2 = LOADADDR(.sram2);
/* CCM-RAM section
*
* IMPORTANT NOTE!
* If initialized variables will be placed in this section,
* the startup code needs to be modified to copy the init-values.
*/
.sram2 :
{
. = ALIGN(8);
_ssram2 = .; /* create a global symbol at sram2 start */
*(.sram2)
*(.sram2*)
. = ALIGN(8);
_esram2 = .; /* create a global symbol at sram2 end */
} >SRAM2 AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
. = ALIGN(4);
_sbss = .;
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
_ebss = .;
__bss_end__ = _ebss;
} >RAM
} > ram
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
._stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
end = .;
_end = .;
. = . + _MIN_STACK_SIZE;
. = ALIGN(8);
} >RAM
} > ram
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}

View File

@ -1,202 +1,80 @@
/*
*****************************************************************************
**
/* Copyright 2019 SoloKeys Developers */
/* */
/* Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or */
/* http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or */
/* http://opensource.org/licenses/MIT>, at your option. This file may not be */
/* copied, modified, or distributed except according to those terms. */
** File : LinkerScript.ld
**
** Abstract : Linker script for STM32L432KCUx Device with
** 256KByte FLASH, 64KByte RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
**
** Distribution: The file is distributed as is, without any warranty
** of any kind.
**
** (c)Copyright Ac6.
** You may use this file as-is or modify it according to the needs of your
** project. Distribution of this file (unmodified or modified) is not
** permitted. Ac6 permit registered System Workbench for MCU users the
** rights to distribute the assembled, compiled & linked contents of this
** file as part of an application binary file, provided that it is built
** using the System Workbench for MCU toolchain.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x2000c000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* End of RAM */
_estack = 0x2000c000;
_MIN_STACK_SIZE = 0x400;
/*
Memory layout of device:
20 KB 198KB-8 38 KB
| bootloader | application | secrets/data |
*/
/* Specify the memory areas */
MEMORY
{
/* First 20 KB is bootloader */
FLASH (rx) : ORIGIN = 0x08005000, LENGTH = 198K-8 /* Leave out 38 Kb at end for data */
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
flash (rx) : ORIGIN = 0x08005000, LENGTH = 198K - 8
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(8);
KEEP(*(.isr_vector)) /* Startup code */
KEEP(*(.isr_vector))
. = ALIGN(8);
} >FLASH
} >flash
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(8);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
*(.text*)
*(.rodata*)
KEEP(*(.init))
KEEP(*(.finit))
. = ALIGN(8);
_etext = .;
} >flash
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(8);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(8);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(8);
} >FLASH
.ARM.extab :
{
. = ALIGN(8);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(8);
} >FLASH
.ARM : {
. = ALIGN(8);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(8);
} >FLASH
.preinit_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(8);
} >FLASH
.init_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(8);
} >FLASH
.fini_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
_sdata = .;
*(.data*)
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
_edata = .;
} >ram AT> flash
_sisram2 = LOADADDR(.sram2);
/* CCM-RAM section
*
* IMPORTANT NOTE!
* If initialized variables will be placed in this section,
* the startup code needs to be modified to copy the init-values.
*/
.sram2 :
{
. = ALIGN(8);
_ssram2 = .; /* create a global symbol at sram2 start */
*(.sram2)
*(.sram2*)
. = ALIGN(8);
_esram2 = .; /* create a global symbol at sram2 end */
} >SRAM2 AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
. = ALIGN(4);
_sbss = .;
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
_ebss = .;
__bss_end__ = _ebss;
} >RAM
} > ram
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
._stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
end = .;
_end = .;
. = . + _MIN_STACK_SIZE;
. = ALIGN(8);
} >RAM
} > ram
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}

View File

@ -1,203 +1,74 @@
/*
*****************************************************************************
**
/* Copyright 2019 SoloKeys Developers */
/* */
/* Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or */
/* http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or */
/* http://opensource.org/licenses/MIT>, at your option. This file may not be */
/* copied, modified, or distributed except according to those terms. */
** File : LinkerScript.ld
**
** Abstract : Linker script for STM32L432KCUx Device with
** 256KByte FLASH, 64KByte RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
**
** Distribution: The file is distributed as is, without any warranty
** of any kind.
**
** (c)Copyright Ac6.
** You may use this file as-is or modify it according to the needs of your
** project. Distribution of this file (unmodified or modified) is not
** permitted. Ac6 permit registered System Workbench for MCU users the
** rights to distribute the assembled, compiled & linked contents of this
** file as part of an application binary file, provided that it is built
** using the System Workbench for MCU toolchain.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x2000c000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* End of RAM */
_estack = 0x2000c000;
_MIN_STACK_SIZE = 0x400;
/* Specify the memory areas */
MEMORY
{
/* First 32 KB is bootloader */
/*FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 238K-8 [> Leave out 38 Kb at end for data <]*/
FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 186K-8 /* Leave out 38 Kb at end for data */
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
flash (rx) : ORIGIN = 0x08008000, LENGTH = 186K - 8
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(8);
KEEP(*(.isr_vector)) /* Startup code */
KEEP(*(.isr_vector))
. = ALIGN(8);
} >FLASH
} >flash
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(8);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
*(.text*)
*(.rodata*)
KEEP(*(.init))
KEEP(*(.finit))
. = ALIGN(8);
_etext = .;
} >flash
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(8);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(8);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(8);
} >FLASH
.ARM.extab :
{
. = ALIGN(8);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(8);
} >FLASH
.ARM : {
. = ALIGN(8);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(8);
} >FLASH
.preinit_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(8);
} >FLASH
.init_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(8);
} >FLASH
.fini_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
_sdata = .;
*(.data*)
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
_edata = .;
} >ram AT> flash
_sisram2 = LOADADDR(.sram2);
/* CCM-RAM section
*
* IMPORTANT NOTE!
* If initialized variables will be placed in this section,
* the startup code needs to be modified to copy the init-values.
*/
.sram2 :
{
. = ALIGN(8);
_ssram2 = .; /* create a global symbol at sram2 start */
*(.sram2)
*(.sram2*)
. = ALIGN(8);
_esram2 = .; /* create a global symbol at sram2 end */
} >SRAM2 AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
. = ALIGN(4);
_sbss = .;
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
_ebss = .;
__bss_end__ = _ebss;
} >RAM
} > ram
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
._stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
end = .;
_end = .;
. = . + _MIN_STACK_SIZE;
. = ALIGN(8);
} >RAM
} > ram
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}

366
targets/stm32l432/src/ams.c Normal file
View File

@ -0,0 +1,366 @@
#include <string.h>
#include "stm32l4xx_ll_spi.h"
#include "ams.h"
#include "log.h"
#include "util.h"
#include "device.h"
#include "nfc.h"
static void flush_rx()
{
while(LL_SPI_IsActiveFlag_RXNE(SPI1) != 0)
{
LL_SPI_ReceiveData8(SPI1);
}
}
static void wait_for_tx()
{
// while (LL_SPI_IsActiveFlag_BSY(SPI1) == 1)
// ;
while(LL_SPI_GetTxFIFOLevel(SPI1) != LL_SPI_TX_FIFO_EMPTY)
;
}
static void wait_for_rx()
{
while(LL_SPI_IsActiveFlag_RXNE(SPI1) == 0)
;
}
void ams_print_device(AMS_DEVICE * dev)
{
printf1(TAG_NFC, "AMS_DEVICE:\r\n");
printf1(TAG_NFC, " io_conf: %02x\r\n",dev->regs.io_conf);
printf1(TAG_NFC, " ic_conf0: %02x\r\n",dev->regs.ic_conf0);
printf1(TAG_NFC, " ic_conf1: %02x\r\n",dev->regs.ic_conf1);
printf1(TAG_NFC, " ic_conf2: %02x\r\n",dev->regs.ic_conf2);
printf1(TAG_NFC, " rfid_status: %02x\r\n",dev->regs.rfid_status);
printf1(TAG_NFC, " ic_status: %02x\r\n",dev->regs.ic_status);
printf1(TAG_NFC, " mask_int0: %02x\r\n",dev->regs.mask_int0);
printf1(TAG_NFC, " mask_int1: %02x\r\n",dev->regs.mask_int1);
printf1(TAG_NFC, " int0: %02x\r\n",dev->regs.int0);
printf1(TAG_NFC, " int1: %02x\r\n",dev->regs.int1);
printf1(TAG_NFC, " buffer_status2: %02x\r\n",dev->regs.buffer_status2);
printf1(TAG_NFC, " buffer_status1: %02x\r\n",dev->regs.buffer_status1);
printf1(TAG_NFC, " last_nfc_addr: %02x\r\n",dev->regs.last_nfc_addr);
printf1(TAG_NFC, " product_type: %02x\r\n",dev->regs.product_type);
printf1(TAG_NFC, " product_subtype:%02x\r\n",dev->regs.product_subtype);
printf1(TAG_NFC, " version_maj: %02x\r\n",dev->regs.version_maj);
printf1(TAG_NFC, " version_min: %02x\r\n",dev->regs.version_min);
}
static uint8_t send_recv(uint8_t b)
{
wait_for_tx();
LL_SPI_TransmitData8(SPI1, b);
wait_for_rx();
b = LL_SPI_ReceiveData8(SPI1);
return b;
}
void ams_write_reg(uint8_t addr, uint8_t tx)
{
send_recv(0x00| addr);
send_recv(tx);
UNSELECT();
SELECT();
}
uint8_t ams_read_reg(uint8_t addr)
{
send_recv(0x20| (addr & 0x1f));
uint8_t data = send_recv(0);
UNSELECT();
SELECT();
return data;
}
// data must be 14 bytes long
void read_reg_block(AMS_DEVICE * dev)
{
int i;
uint8_t mode = 0x20 | (4 );
flush_rx();
send_recv(mode);
for (i = 0x04; i < 0x0d; i++)
{
dev->buf[i] = send_recv(0);
}
UNSELECT();
SELECT();
}
void ams_read_buffer(uint8_t * data, int len)
{
send_recv(0xa0);
while(len--)
{
*data++ = send_recv(0x00);
}
UNSELECT();
SELECT();
}
void ams_write_buffer(uint8_t * data, int len)
{
send_recv(0x80);
while(len--)
{
send_recv(*data++);
}
UNSELECT();
SELECT();
}
// data must be 4 bytes
void ams_read_eeprom_block(uint8_t block, uint8_t * data)
{
send_recv(0x7f);
send_recv(block << 1);
data[0] = send_recv(0);
data[1] = send_recv(0);
data[2] = send_recv(0);
data[3] = send_recv(0);
UNSELECT();
SELECT();
}
// data must be 4 bytes
void ams_write_eeprom_block(uint8_t block, uint8_t * data)
{
send_recv(0x40);
send_recv(block << 1);
send_recv(data[0]);
send_recv(data[1]);
send_recv(data[2]);
send_recv(data[3]);
UNSELECT();
SELECT();
}
void ams_write_command(uint8_t cmd)
{
send_recv(0xc0 | cmd);
UNSELECT();
SELECT();
}
const char * ams_get_state_string(uint8_t regval)
{
if (regval & AMS_STATE_INVALID)
{
return "STATE_INVALID";
}
switch (regval & AMS_STATE_MASK)
{
case AMS_STATE_OFF:
return "STATE_OFF";
case AMS_STATE_SENSE:
return "STATE_SENSE";
case AMS_STATE_RESOLUTION:
return "STATE_RESOLUTION";
case AMS_STATE_RESOLUTION_L2:
return "STATE_RESOLUTION_L2";
case AMS_STATE_SELECTED:
return "STATE_SELECTED";
case AMS_STATE_SECTOR2:
return "STATE_SECTOR2";
case AMS_STATE_SECTORX_2:
return "STATE_SECTORX_2";
case AMS_STATE_SELECTEDX:
return "STATE_SELECTEDX";
case AMS_STATE_SENSEX_L2:
return "STATE_SENSEX_L2";
case AMS_STATE_SENSEX:
return "STATE_SENSEX";
case AMS_STATE_SLEEP:
return "STATE_SLEEP";
}
return "STATE_WRONG";
}
int ams_state_is_valid(uint8_t regval)
{
if (regval & AMS_STATE_INVALID)
{
return 0;
}
switch (regval & AMS_STATE_MASK)
{
case AMS_STATE_OFF:
case AMS_STATE_SENSE:
case AMS_STATE_RESOLUTION:
case AMS_STATE_RESOLUTION_L2:
case AMS_STATE_SELECTED:
case AMS_STATE_SECTOR2:
case AMS_STATE_SECTORX_2:
case AMS_STATE_SELECTEDX:
case AMS_STATE_SENSEX_L2:
case AMS_STATE_SENSEX:
case AMS_STATE_SLEEP:
return 1;
}
return 0;
}
void ams_print_int0(uint8_t int0)
{
#if DEBUG_LEVEL
uint32_t tag = (TAG_NFC)|(TAG_NO_TAG);
printf1(TAG_NFC," ");
if (int0 & AMS_INT_XRF)
printf1(tag," XRF");
if (int0 & AMS_INT_TXE)
printf1(tag," TXE");
if (int0 & AMS_INT_RXE)
printf1(tag," RXE");
if (int0 & AMS_INT_EER_RF)
printf1(tag," EER_RF");
if (int0 & AMS_INT_EEW_RF)
printf1(tag," EEW_RF");
if (int0 & AMS_INT_SLP)
printf1(tag," SLP");
if (int0 & AMS_INT_WU_A)
printf1(tag," WU_A");
if (int0 & AMS_INT_INIT)
printf1(tag," INIT");
printf1(tag,"\r\n");
#endif
}
void ams_print_int1(uint8_t int0)
{
#if DEBUG_LEVEL
uint32_t tag = (TAG_NFC)|(TAG_NO_TAG);
printf1(TAG_NFC," ");
if (int0 & AMS_INT_ACC_ERR)
printf1(tag," ACC_ERR");
if (int0 & AMS_INT_EEAC_ERR)
printf1(tag," EEAC_ERR");
if (int0 & AMS_INT_IO_EEWR)
printf1(tag," IO_EEWR");
if (int0 & AMS_INT_BF_ERR)
printf1(tag," BF_ERR");
if (int0 & AMS_INT_CRC_ERR)
printf1(tag," CRC_ERR");
if (int0 & AMS_INT_PAR_ERR)
printf1(tag," PAR_ERR");
if (int0 & AMS_INT_FRM_ERR)
printf1(tag," FRM_ERR");
if (int0 & AMS_INT_RXS)
printf1(tag," RXS");
printf1(tag,"\r\n");
#endif
}
void ams_init()
{
LL_GPIO_SetPinMode(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN,LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetOutputPin(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN);
LL_SPI_SetClockPolarity(SPI1,LL_SPI_POLARITY_LOW);
LL_SPI_SetClockPhase(SPI1,LL_SPI_PHASE_2EDGE);
LL_SPI_SetRxFIFOThreshold(SPI1,LL_SPI_RX_FIFO_TH_QUARTER);
LL_SPI_Enable(SPI1);
// delay(10);
SELECT();
delay(1);
}
void ams_configure()
{
// Should not be used during passive operation.
uint8_t block[4];
// check connection
uint8_t productType = ams_read_reg(AMS_REG_PRODUCT_TYPE);
if (productType != 0x14)
{
printf1(TAG_ERR, "Have wrong product type [0x%02x]. AMS3956 connection error.\n", productType);
}
printf1(TAG_NFC,"AMS3956 product type 0x%02x.\n", productType);
ams_read_eeprom_block(AMS_CONFIG_UID_ADDR, block);
printf1(TAG_NFC,"UID: 3F 14 02 - "); dump_hex1(TAG_NFC,block,4);
ams_read_eeprom_block(AMS_CONFIG_BLOCK0_ADDR, block);
printf1(TAG_NFC,"conf0: "); dump_hex1(TAG_NFC,block,4);
uint8_t sense1 = 0x44;
uint8_t sense2 = 0x00;
uint8_t selr = 0x20; // SAK
if(block[0] != sense1 || block[1] != sense2 || block[2] != selr)
{
printf1(TAG_NFC,"Writing config block 0\r\n");
block[0] = sense1;
block[1] = sense2;
block[2] = selr;
block[3] = 0x00;
ams_write_eeprom_block(AMS_CONFIG_BLOCK0_ADDR, block);
UNSELECT();
delay(10);
SELECT();
delay(10);
ams_read_eeprom_block(AMS_CONFIG_BLOCK0_ADDR, block);
printf1(TAG_NFC,"conf0: "); dump_hex1(TAG_NFC,block,4);
}
ams_read_eeprom_block(AMS_CONFIG_BLOCK1_ADDR, block);
printf1(TAG_NFC,"conf1: "); dump_hex1(TAG_NFC,block,4);
uint8_t ic_cfg1 = AMS_CFG1_OUTPUT_RESISTANCE_100 | AMS_CFG1_VOLTAGE_LEVEL_2V0;
uint8_t ic_cfg2 = AMS_CFG2_TUN_MOD;
if (block[0] != ic_cfg1 || block[1] != ic_cfg2)
{
printf1(TAG_NFC,"Writing config block 1\r\n");
ams_write_reg(AMS_REG_IC_CONF1,ic_cfg1);
ams_write_reg(AMS_REG_IC_CONF2,ic_cfg2);
// set IC_CFG1
block[0] = ic_cfg1;
// set IC_CFG2
block[1] = ic_cfg2;
// mask interrupt bits
block[2] = 0x80;
block[3] = 0;
ams_write_eeprom_block(AMS_CONFIG_BLOCK1_ADDR, block);
UNSELECT();
delay(10);
SELECT();
delay(10);
ams_read_eeprom_block(0x7F, block);
printf1(TAG_NFC,"conf1: "); dump_hex1(TAG_NFC,block,4);
}
}

162
targets/stm32l432/src/ams.h Normal file
View File

@ -0,0 +1,162 @@
// AS3956 interface
// https://ams.com/as3956
// https://ams.com/documents/20143/36005/AS3956_DS000546_7-00.pdf
#ifndef _AMS_H_
#define _AMS_H_
#include <stdint.h>
#include <stdbool.h>
#include "stm32l4xx_ll_gpio.h"
typedef union
{
uint8_t buf[0x20];
struct {
uint8_t io_conf; // 0x00
uint8_t ic_conf0; // 0x01
uint8_t ic_conf1; // 0x02
uint8_t ic_conf2; // 0x03
uint8_t rfid_status; // 0x04
uint8_t ic_status; // 0x05
uint8_t _nc0[2]; // 0x06 - 0x07
uint8_t mask_int0; // 0x08
uint8_t mask_int1; // 0x09
uint8_t int0; // 0x0a
uint8_t int1; // 0x0b
uint8_t buffer_status2; // 0x0c
uint8_t buffer_status1; // 0x0d
uint8_t last_nfc_addr; // 0x0e
uint8_t _nc1[0x1b - 0x0f + 1]; // 0x0f - 0x1b
uint8_t product_type; // 0x1c
uint8_t product_subtype; // 0x1d
uint8_t version_maj; // 0x1e
uint8_t version_min; // 0x1f
} regs;
} __attribute__((packed)) AMS_DEVICE;
#define SELECT() LL_GPIO_ResetOutputPin(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN)
#define UNSELECT() LL_GPIO_SetOutputPin(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN)
void ams_init();
void ams_configure();
void ams_read_buffer(uint8_t * data, int len);
void ams_write_buffer(uint8_t * data, int len);
void ams_write_command(uint8_t cmd);
void read_reg_block(AMS_DEVICE * dev);
uint8_t ams_read_reg(uint8_t addr);
void ams_write_reg(uint8_t addr, uint8_t tx);
const char * ams_get_state_string(uint8_t regval);
int ams_state_is_valid(uint8_t regval);
#define AMS_REG_IO_CONF 0x00
#define AMS_REG_IC_CONF0 0x01
#define AMS_REG_IC_CONF1 0x02
#define AMS_REG_IC_CONF2 0x03
#define AMS_RFCFG_EN 0x80
#define AMS_TUN_MOD 0x40
#define AMS_REG_RFID_STATUS 0x04
#define AMS_HF_PON 0x80
#define AMS_STATE_MASK 0x78
#define AMS_STATE_INVALID 0x04
#define AMS_STATE_OFF (0 << 3)
#define AMS_STATE_SENSE (1 << 3)
#define AMS_STATE_RESOLUTION (3 << 3)
#define AMS_STATE_RESOLUTION_L2 (2 << 3)
#define AMS_STATE_SELECTED (6 << 3)
#define AMS_STATE_SECTOR2 (7 << 3)
#define AMS_STATE_SECTORX_2 (0xf << 3)
#define AMS_STATE_SELECTEDX (0xe << 3)
#define AMS_STATE_SENSEX_L2 (0xa << 3)
#define AMS_STATE_SENSEX (0xb << 3)
#define AMS_STATE_SLEEP (0x9 << 3)
// ... //
#define AMS_REG_MASK_INT0 0x08
#define AMS_MASK0_PU (1<<7) // power up
#define AMS_MASK0_WU_A (1<<6) // selected INT
#define AMS_MASK0_SLP (1<<5)
#define AMS_MASK0_EEW_RF (1<<4)
#define AMS_MASK0_EER_RF (1<<3)
#define AMS_MASK0_RXE (1<<2)
#define AMS_MASK0_TXE (1<<1)
#define AMS_MASK0_XRF (1<<0)
#define AMS_REG_MASK_INT1 0x09
#define AMS_REG_INT0 0x0a
#define AMS_INT_XRF (1<<0)
#define AMS_INT_TXE (1<<1)
#define AMS_INT_RXE (1<<2)
#define AMS_INT_EER_RF (1<<3)
#define AMS_INT_EEW_RF (1<<4)
#define AMS_INT_SLP (1<<5)
#define AMS_INT_WU_A (1<<6)
#define AMS_INT_INIT (1<<7)
#define AMS_REG_INT1 0x0b
#define AMS_INT_ACC_ERR (1<<0)
#define AMS_INT_EEAC_ERR (1<<1)
#define AMS_INT_IO_EEWR (1<<2)
#define AMS_INT_BF_ERR (1<<3)
#define AMS_INT_CRC_ERR (1<<4)
#define AMS_INT_PAR_ERR (1<<5)
#define AMS_INT_FRM_ERR (1<<6)
#define AMS_INT_RXS (1<<7)
#define AMS_REG_BUF2 0x0c
#define AMS_BUF_LEN_MASK 0x1f
#define AMS_BUF_INVALID 0x80
#define AMS_REG_BUF1 0x0d
// ... //
#define AMS_REG_PRODUCT_TYPE 0x1c
#define AMS_REG_PRODUCT_SUBTYPE 0x1d
#define AMS_REG_VERSION_MAJOR 0x1e
#define AMS_REG_VERSION_MINOR 0x1f
#define AMS_CONFIG_UID_ADDR 0x00
#define AMS_CONFIG_BLOCK0_ADDR 0x7e
#define AMS_CONFIG_BLOCK1_ADDR 0x7f
#define AMS_CFG1_VOLTAGE_LEVEL_1V9 (0x00<<2)
#define AMS_CFG1_VOLTAGE_LEVEL_2V0 (0x01<<2)
#define AMS_CFG1_VOLTAGE_LEVEL_2V1 (0x02<<2)
#define AMS_CFG1_VOLTAGE_LEVEL_2V2 (0x03<<2)
#define AMS_CFG1_VOLTAGE_LEVEL_2V3 (0x04<<2)
#define AMS_CFG1_VOLTAGE_LEVEL_2V4 (0x05<<2)
#define AMS_CFG1_VOLTAGE_LEVEL_2V5 (0x06<<2)
#define AMS_CFG1_VOLTAGE_LEVEL_2V6 (0x07<<2)
#define AMS_CFG1_VOLTAGE_LEVEL_2V7 (0x08<<2)
#define AMS_CFG1_VOLTAGE_LEVEL_2V8 (0x09<<2)
#define AMS_CFG1_VOLTAGE_LEVEL_2V9 (0x0a<<2)
#define AMS_CFG1_VOLTAGE_LEVEL_3V0 (0x0b<<2)
#define AMS_CFG1_OUTPUT_RESISTANCE_ZZ 0x00
#define AMS_CFG1_OUTPUT_RESISTANCE_100 0x01
#define AMS_CFG1_OUTPUT_RESISTANCE_50 0x02
#define AMS_CFG1_OUTPUT_RESISTANCE_25 0x03
#define AMS_CFG2_RFCFG_EN (1<<7)
#define AMS_CFG2_TUN_MOD (1<<6)
#define AMS_CMD_DEFAULT 0x02
#define AMS_CMD_CLEAR_BUFFER 0x04
#define AMS_CMD_RESTART_TRANSCEIVER 0x06
#define AMS_CMD_DIS_EN_TRANSCEIVER 0x07
#define AMS_CMD_TRANSMIT_BUFFER 0x08
#define AMS_CMD_TRANSMIT_ACK 0x09
#define AMS_CMD_TRANSMIT_NACK0 0x0A
#define AMS_CMD_TRANSMIT_NACK1 0x0B
#define AMS_CMD_TRANSMIT_NACK4 0x0D
#define AMS_CMD_TRANSMIT_NACK5 0x0C
#define AMS_CMD_SLEEP 0x10
#define AMS_CMD_SENSE 0x11
#define AMS_CMD_SENSE_SLEEP 0x12
#endif

View File

@ -30,6 +30,7 @@
// #define DISABLE_CTAPHID_WINK
// #define DISABLE_CTAPHID_CBOR
#define ENABLE_SERIAL_PRINTING
#if defined(SOLO_HACKER)
#define SOLO_PRODUCT_NAME "Solo Hacker " SOLO_VERSION
@ -38,7 +39,7 @@
#endif
void printing_init();
void hw_init(void);
void hw_init(int lf);
//#define TEST
//#define TEST_POWER
@ -63,6 +64,12 @@ void hw_init(void);
#define SOLO_BUTTON_PORT GPIOA
#define SOLO_BUTTON_PIN LL_GPIO_PIN_0
#define SOLO_AMS_CS_PORT GPIOB
#define SOLO_AMS_CS_PIN LL_GPIO_PIN_0
#define SOLO_AMS_IRQ_PORT GPIOC
#define SOLO_AMS_IRQ_PIN LL_GPIO_PIN_15
#define SKIP_BUTTON_CHECK_WITH_DELAY 0
#define SKIP_BUTTON_CHECK_FAST 0

View File

@ -7,6 +7,50 @@
#include <stdint.h>
#include "crypto.h"
#ifdef USE_SOLOKEYS_CERT
const uint8_t attestation_cert_der[] =
"\x30\x82\x02\xe1\x30\x82\x02\x88\xa0\x03\x02\x01\x02\x02\x01\x01\x30\x0a\x06\x08"
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55\x04\x06"
"\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79\x6c"
"\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f\x20\x4b"
"\x65\x79\x73\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x52\x6f\x6f\x74\x20\x43"
"\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65\x79\x73"
"\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16"
"\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f\x6d\x30"
"\x20\x17\x0d\x31\x38\x31\x31\x31\x31\x31\x32\x35\x32\x30\x30\x5a\x18\x0f\x32\x30"
"\x36\x38\x31\x30\x32\x39\x31\x32\x35\x32\x30\x30\x5a\x30\x81\x92\x31\x0b\x30\x09"
"\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08"
"\x4d\x61\x72\x79\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53"
"\x6f\x6c\x6f\x20\x4b\x65\x79\x73\x31\x22\x30\x20\x06\x03\x55\x04\x0b\x0c\x19\x41"
"\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72\x20\x41\x74\x74\x65\x73\x74\x61"
"\x74\x69\x6f\x6e\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b"
"\x65\x79\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01"
"\x09\x01\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63"
"\x6f\x6d\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x22\xfe\x0f\xb5\x2a\x78\xbe\xc6\x45\x37\x1a"
"\x28\xa7\x57\x43\x49\xa4\x6f\x85\x4d\xca\x4e\x25\x1c\x9f\x75\x30\x3d\xbf\x10\xd5"
"\xd2\xd2\x0b\xb9\x69\x2c\xdd\xb2\x5c\x14\xd8\x39\x85\x12\xf6\x23\xee\x91\xba\xc6"
"\xac\xff\x4a\x1a\x27\xef\xe0\xc1\x54\x3f\xd4\xd9\xc5\xa3\x81\xdc\x30\x81\xd9\x30"
"\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x3b\xe6\xd2\xc0\x6f\xf2\xe7\xb0\x7c\x9d"
"\x9e\x28\xc0\x20\xb0\x0d\x07\xc8\x15\xc8\x30\x81\x9f\x06\x03\x55\x1d\x23\x04\x81"
"\x97\x30\x81\x94\xa1\x81\x86\xa4\x81\x83\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55"
"\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72"
"\x79\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f"
"\x20\x4b\x65\x79\x73\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x52\x6f\x6f\x74"
"\x20\x43\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65"
"\x79\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09"
"\x01\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f"
"\x6d\x82\x09\x00\xc4\x47\x63\x92\x8f\xf4\xbe\x8c\x30\x09\x06\x03\x55\x1d\x13\x04"
"\x02\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04\x04\x03\x02\x04\xf0\x30\x0a\x06\x08"
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x47\x00\x30\x44\x02\x20\x71\x10\x46\x2c\xf5"
"\x16\x18\x97\x55\xca\x64\x50\x3b\x69\xb2\xdf\x17\x71\xab\xad\x8e\xc0\xd6\xa6\x07"
"\x3d\x66\x8a\x3b\xbb\xfe\x61\x02\x20\x1e\x82\xef\xeb\x5e\x4e\x3a\x00\x84\x64\xd2"
"\xf8\x84\xc3\x78\x35\x93\x63\x81\x2e\xbe\xa6\x12\x32\x6e\x29\x90\xc8\x91\x4b\x71"
"\x52"
;
#else
// For testing/development only
const uint8_t attestation_cert_der[] =
@ -50,9 +94,8 @@ const uint8_t attestation_cert_der[] =
"\xf3\x87\x61\x82\xd8\xcd\x48\xfc\x57"
;
#endif
const uint16_t attestation_cert_der_size = sizeof(attestation_cert_der)-1;
const uint8_t attestation_key[] = "\x1b\x26\x26\xec\xc8\xf6\x9b\x0f\x69\xe3\x4f\xb2\x36\xd7\x64\x66\xba\x12\xac\x16\xc3\xab\x57\x50\xba\x06\x4e\x8b\x90\xe0\x24\x48";
const uint16_t attestation_key_size = sizeof(attestation_key)-1;
const uint16_t attestation_key_size = 32;

View File

@ -24,6 +24,9 @@
#include "aes.h"
#include "ctap.h"
#include "device.h"
// stuff for SHA512
#include "sha2.h"
#include "blockwise.h"
#include APP_CONFIG
#include "log.h"
#include "memory_layout.h"
@ -48,6 +51,7 @@ typedef enum
static SHA256_CTX sha256_ctx;
static cf_sha512_context sha512_ctx;
static const struct uECC_Curve_t * _es256_curve = NULL;
static const uint8_t * _signing_key = NULL;
static int _key_len = 0;
@ -62,6 +66,9 @@ void crypto_sha256_init()
sha256_init(&sha256_ctx);
}
void crypto_sha512_init() {
cf_sha512_init(&sha512_ctx);
}
void crypto_load_master_secret(uint8_t * key)
{
@ -86,6 +93,10 @@ void crypto_sha256_update(uint8_t * data, size_t len)
sha256_update(&sha256_ctx, data, len);
}
void crypto_sha512_update(const uint8_t * data, size_t len) {
cf_sha512_update(&sha512_ctx, data, len);
}
void crypto_sha256_update_secret()
{
sha256_update(&sha256_ctx, master_secret, 32);
@ -96,6 +107,11 @@ void crypto_sha256_final(uint8_t * hash)
sha256_final(&sha256_ctx, hash);
}
void crypto_sha512_final(uint8_t * hash) {
// NB: there is also cf_sha512_digest
cf_sha512_digest_final(&sha512_ctx, hash);
}
void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
{
uint8_t buf[64];

View File

@ -10,6 +10,7 @@
#include "stm32l4xx_ll_gpio.h"
#include "stm32l4xx_ll_tim.h"
#include "stm32l4xx_ll_usart.h"
#include "stm32l4xx_ll_pwr.h"
#include "usbd_hid.h"
#include APP_CONFIG
@ -26,6 +27,11 @@
#include "memory_layout.h"
#include "stm32l4xx_ll_iwdg.h"
#include "usbd_cdc_if.h"
#include "nfc.h"
#include "init.h"
#define LOW_FREQUENCY 1
#define HIGH_FREQUENCY 0
void wait_for_usb_tether();
@ -34,6 +40,8 @@ uint32_t __90_ms = 0;
uint32_t __device_status = 0;
uint32_t __last_update = 0;
extern PCD_HandleTypeDef hpcd;
static bool haveNFC = 0;
static bool isLowFreq = 0;
#define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN))
@ -50,6 +58,13 @@ void TIM6_DAC_IRQHandler()
ctaphid_update_status(__device_status);
}
}
#ifndef IS_BOOTLOADER
// NFC sending WTX if needs
if (device_is_nfc())
{
WTX_timer_exec();
}
#endif
}
// Global USB interrupt handler
@ -91,32 +106,42 @@ void device_reboot()
{
NVIC_SystemReset();
}
void device_init()
{
hw_init();
LL_GPIO_SetPinMode(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_MODE_INPUT);
LL_GPIO_SetPinPull(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_PULL_UP);
#ifndef IS_BOOTLOADER
hw_init(LOW_FREQUENCY);
isLowFreq = 0;
haveNFC = nfc_init();
if (haveNFC)
{
printf1(TAG_NFC, "Have NFC\r\n");
}
else
{
printf1(TAG_NFC, "Have NO NFC\r\n");
hw_init(HIGH_FREQUENCY);
isLowFreq = 0;
}
usbhid_init();
ctaphid_init();
ctap_init();
#if BOOT_TO_DFU
flash_option_bytes_init(1);
#else
flash_option_bytes_init(0);
#endif
#endif
printf1(TAG_GEN,"hello solo\r\n");
}
void usb_init(void);
void usbhid_init()
bool device_is_nfc()
{
usb_init();
#if DEBUG_LEVEL>1
wait_for_usb_tether();
#endif
return haveNFC;
}
void wait_for_usb_tether()
@ -130,6 +155,26 @@ void wait_for_usb_tether()
;
}
void usbhid_init()
{
if (!isLowFreq)
{
init_usb();
#if DEBUG_LEVEL>1
wait_for_usb_tether();
#endif
}
else
{
}
}
int usbhid_recv(uint8_t * msg)
{
if (fifo_hidmsg_size())
@ -366,6 +411,7 @@ uint32_t ctap_atomic_count(int sel)
}
void device_manage()
{
#if NON_BLOCK_PRINTING
@ -386,6 +432,10 @@ void device_manage()
}
}
#endif
#ifndef IS_BOOTLOADER
// if(device_is_nfc())
nfc_loop();
#endif
}
static int handle_packets()
@ -543,7 +593,7 @@ void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
memmove(tmppage + (sizeof(CTAP_residentKey) * index) % PAGE_SIZE, rk, sizeof(CTAP_residentKey));
flash_erase_page(page);
flash_write(flash_addr(page), tmppage, ((sizeof(CTAP_residentKey) * (index + 1)) % PAGE_SIZE) );
flash_write(flash_addr(page), tmppage, PAGE_SIZE);
}
else
{

View File

@ -18,6 +18,7 @@
#include "stm32l4xx_ll_bus.h"
#include "stm32l4xx_ll_tim.h"
#include "stm32l4xx_ll_rng.h"
#include "stm32l4xx_ll_spi.h"
#include "stm32l4xx_ll_usb.h"
#include "stm32l4xx_hal_pcd.h"
#include "stm32l4xx_hal.h"
@ -29,57 +30,86 @@
#include "usbd_composite.h"
#include "usbd_cdc_if.h"
#include "device.h"
#include "init.h"
#include APP_CONFIG
/* USER CODE BEGIN Includes */
// KHz
#define MAX_CLOCK_RATE 24000
/* USER CODE END Includes */
#define SET_CLOCK_RATE2() SystemClock_Config()
/* Private variables ---------------------------------------------------------*/
#if MAX_CLOCK_RATE == 48000
#define SET_CLOCK_RATE0() SystemClock_Config_LF32()
#define SET_CLOCK_RATE1() SystemClock_Config_LF48()
#elif MAX_CLOCK_RATE == 32000
#define SET_CLOCK_RATE0() SystemClock_Config_LF24()
#define SET_CLOCK_RATE1() SystemClock_Config_LF32()
#elif MAX_CLOCK_RATE == 28000
#define SET_CLOCK_RATE0() SystemClock_Config_LF24()
#define SET_CLOCK_RATE1() SystemClock_Config_LF28()
#elif MAX_CLOCK_RATE == 24000
#define SET_CLOCK_RATE0() SystemClock_Config_LF16()
#define SET_CLOCK_RATE1() SystemClock_Config_LF24()
#elif MAX_CLOCK_RATE == 20000
#define SET_CLOCK_RATE0() SystemClock_Config_LF16()
#define SET_CLOCK_RATE1() SystemClock_Config_LF20()
#elif MAX_CLOCK_RATE == 16000
#define SET_CLOCK_RATE0() SystemClock_Config_LF8()
#define SET_CLOCK_RATE1() SystemClock_Config_LF16()
#else
#error "Invalid clock rate selected"
#endif
USBD_HandleTypeDef Solo_USBD_Device;
/* Private function prototypes -----------------------------------------------*/
static void LL_Init(void);
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
#if DEBUG_LEVEL > 0
static void MX_USART1_UART_Init(void);
#endif
static void MX_TIM2_Init(void);
static void MX_TIM6_Init(void);
static void MX_RNG_Init(void);
#define Error_Handler() _Error_Handler(__FILE__,__LINE__)
void _Error_Handler(char *file, int line);
void SystemClock_Config(void);
void SystemClock_Config_LF16(void);
void SystemClock_Config_LF20(void);
void SystemClock_Config_LF24(void);
void SystemClock_Config_LF28(void);
void SystemClock_Config_LF48(void);
void hw_init(void)
void hw_init(int lowfreq)
{
#ifdef IS_BOOTLOADER
SCB->VTOR = FLASH_BASE;
#else
#endif
LL_Init();
init_gpio();
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
if (lowfreq)
{
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE2); // Under voltage
device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE2);
}
else
{
SystemClock_Config();
}
SystemClock_Config(); // TODO bootloader should not change clk freq.
MX_GPIO_Init();
MX_TIM2_Init(); // PWM for LEDs
MX_TIM6_Init(); // ~1 ms timer
if (!lowfreq)
{
init_pwm();
}
init_millisecond_timer(lowfreq);
#if DEBUG_LEVEL > 0
MX_USART1_UART_Init();// debug uart
init_debug_uart();
#endif
MX_RNG_Init();
init_rng();
init_spi();
TIM6->SR = 0;
__enable_irq();
NVIC_EnableIRQ(TIM6_IRQn);
}
static void LL_Init(void)
@ -107,12 +137,29 @@ static void LL_Init(void)
}
void device_set_clock_rate(DEVICE_CLOCK_RATE param)
{
switch(param)
{
case DEVICE_LOW_POWER_IDLE:
SET_CLOCK_RATE0();
break;
case DEVICE_LOW_POWER_FAST:
SET_CLOCK_RATE1();
break;
case DEVICE_FAST:
SET_CLOCK_RATE2();
break;
}
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
LL_FLASH_SetLatency(LL_FLASH_LATENCY_2);
@ -129,8 +176,15 @@ void SystemClock_Config(void)
{
}
LL_RCC_MSI_Enable();
LL_RCC_LSI_Enable();
/* Wait till LSI is ready */
while(LL_RCC_LSI_IsReady() != 1)
{
}
LL_RCC_MSI_Enable();
/* Wait till MSI is ready */
while(LL_RCC_MSI_IsReady() != 1)
{
@ -187,7 +241,463 @@ void SystemClock_Config(void)
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
}
void usb_init()
void SystemClock_Config_LF4(void)
{
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
LL_RCC_LSI_Enable();
/* Wait till LSI is ready */
while(LL_RCC_LSI_IsReady() != 1)
{
}
LL_RCC_MSI_Enable();
/* Wait till MSI is ready */
while(LL_RCC_MSI_IsReady() != 1)
{
}
LL_RCC_MSI_EnableRangeSelection();
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_6);
LL_RCC_MSI_SetCalibTrimming(0);
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI);
/* Wait till System clock is ready */
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI)
{
}
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
LL_Init1msTick(4000000);
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
LL_SetSystemCoreClock(4000000);
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
/* SysTick_IRQn interrupt configuration */
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
LL_FLASH_SetLatency(LL_FLASH_LATENCY_0);
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0)
{
Error_Handler();
}
}
// 8MHz
void SystemClock_Config_LF8(void)
{
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
LL_RCC_LSI_Enable();
/* Wait till LSI is ready */
while(LL_RCC_LSI_IsReady() != 1)
{
}
LL_RCC_MSI_Enable();
/* Wait till MSI is ready */
while(LL_RCC_MSI_IsReady() != 1)
{
}
LL_RCC_MSI_EnableRangeSelection();
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_7);
LL_RCC_MSI_SetCalibTrimming(0);
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI);
/* Wait till System clock is ready */
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI)
{
}
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
LL_Init1msTick(8000000);
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
LL_SetSystemCoreClock(8000000);
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
/* SysTick_IRQn interrupt configuration */
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
LL_FLASH_SetLatency(LL_FLASH_LATENCY_0);
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0)
{
Error_Handler();
}
}
// 16MHz
void SystemClock_Config_LF16(void)
{
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE2);
LL_RCC_LSI_Enable();
/* Wait till LSI is ready */
while(LL_RCC_LSI_IsReady() != 1)
{
}
LL_RCC_MSI_Enable();
/* Wait till MSI is ready */
while(LL_RCC_MSI_IsReady() != 1)
{
}
LL_RCC_MSI_EnableRangeSelection();
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_8);
LL_RCC_MSI_SetCalibTrimming(0);
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI);
/* Wait till System clock is ready */
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI)
{
}
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_8);
LL_Init1msTick(16000000);
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
LL_SetSystemCoreClock(16000000);
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
/* SysTick_IRQn interrupt configuration */
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
LL_FLASH_SetLatency(LL_FLASH_LATENCY_0);
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0)
{
Error_Handler();
}
}
// 24 MHz
void SystemClock_Config_LF24(void)
{
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
LL_FLASH_SetLatency(LL_FLASH_LATENCY_1);
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_1)
{
Error_Handler();
}
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE2);
LL_RCC_LSI_Enable();
/* Wait till LSI is ready */
while(LL_RCC_LSI_IsReady() != 1)
{
}
LL_RCC_MSI_Enable();
/* Wait till MSI is ready */
while(LL_RCC_MSI_IsReady() != 1)
{
}
LL_RCC_MSI_EnableRangeSelection();
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_9);
LL_RCC_MSI_SetCalibTrimming(0);
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI);
/* Wait till System clock is ready */
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI)
{
}
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_8);
LL_Init1msTick(24000000);
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
LL_SetSystemCoreClock(24000000);
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
/* SysTick_IRQn interrupt configuration */
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
}
// 32 MHz
void SystemClock_Config_LF32(void)
{
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
LL_FLASH_SetLatency(LL_FLASH_LATENCY_1);
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_1)
{
Error_Handler();
}
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
LL_RCC_LSI_Enable();
/* Wait till LSI is ready */
while(LL_RCC_LSI_IsReady() != 1)
{
}
LL_RCC_MSI_Enable();
/* Wait till MSI is ready */
while(LL_RCC_MSI_IsReady() != 1)
{
}
LL_RCC_MSI_EnableRangeSelection();
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_10);
LL_RCC_MSI_SetCalibTrimming(0);
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI);
/* Wait till System clock is ready */
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI)
{
}
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_16);
LL_Init1msTick(32000000);
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
LL_SetSystemCoreClock(32000000);
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
/* SysTick_IRQn interrupt configuration */
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
}
// 28 MHz
void SystemClock_Config_LF28(void)
{
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
LL_FLASH_SetLatency(LL_FLASH_LATENCY_1);
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_1)
{
Error_Handler();
}
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
LL_RCC_HSI_Enable();
/* Wait till HSI is ready */
while(LL_RCC_HSI_IsReady() != 1)
{
}
LL_RCC_HSI_SetCalibTrimming(16);
LL_RCC_LSI_Enable();
/* Wait till LSI is ready */
while(LL_RCC_LSI_IsReady() != 1)
{
}
LL_RCC_MSI_Enable();
/* Wait till MSI is ready */
while(LL_RCC_MSI_IsReady() != 1)
{
}
LL_RCC_MSI_EnableRangeSelection();
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_6);
LL_RCC_MSI_SetCalibTrimming(0);
LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI, LL_RCC_PLLM_DIV_2, 28, LL_RCC_PLLR_DIV_8);
LL_RCC_PLL_EnableDomain_SYS();
LL_RCC_PLL_Enable();
/* Wait till PLL is ready */
while(LL_RCC_PLL_IsReady() != 1)
{
}
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
/* Wait till System clock is ready */
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
{
}
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_8);
LL_Init1msTick(28000000);
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
LL_SetSystemCoreClock(28000000);
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
/* SysTick_IRQn interrupt configuration */
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
}
// 48 MHz
void SystemClock_Config_LF48(void)
{
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
LL_FLASH_SetLatency(LL_FLASH_LATENCY_2);
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_2)
{
Error_Handler();
}
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
LL_RCC_LSI_Enable();
/* Wait till LSI is ready */
while(LL_RCC_LSI_IsReady() != 1)
{
}
LL_RCC_MSI_Enable();
/* Wait till MSI is ready */
while(LL_RCC_MSI_IsReady() != 1)
{
}
LL_RCC_MSI_EnableRangeSelection();
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_11);
LL_RCC_MSI_SetCalibTrimming(0);
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI);
/* Wait till System clock is ready */
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI)
{
}
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_16);
LL_Init1msTick(48000000);
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
LL_SetSystemCoreClock(48000000);
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
/* SysTick_IRQn interrupt configuration */
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
}
// 20 MHz
void SystemClock_Config_LF20(void)
{
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
}
void init_usb()
{
// enable USB power
SET_BIT(PWR->CR2, PWR_CR2_USV);
@ -217,8 +727,7 @@ void usb_init()
USBD_Start(&Solo_USBD_Device);
}
/* TIM2 init function */
static void MX_TIM2_Init(void)
void init_pwm(void)
{
LL_TIM_InitTypeDef TIM_InitStruct;
@ -289,9 +798,7 @@ static void MX_TIM2_Init(void)
}
#if DEBUG_LEVEL > 0
/* USART1 init function */
static void MX_USART1_UART_Init(void)
void init_debug_uart(void)
{
LL_USART_InitTypeDef USART_InitStruct;
@ -301,6 +808,8 @@ static void MX_USART1_UART_Init(void)
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);
LL_USART_DeInit(USART1);
/**USART1 GPIO Configuration
PB6 ------> USART1_TX
PB7 ------> USART1_RX
@ -327,22 +836,37 @@ static void MX_USART1_UART_Init(void)
LL_USART_Enable(USART1);
}
#endif
/** Pinout Configuration
*/
static void MX_GPIO_Init(void)
void init_gpio(void)
{
/* GPIO Ports Clock Enable */
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
LL_GPIO_SetPinMode(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_MODE_INPUT);
LL_GPIO_SetPinPull(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_PULL_UP);
#ifdef SOLO_AMS_IRQ_PORT
// SAVE POWER
// LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
// /**/
// LL_GPIO_InitTypeDef GPIO_InitStruct;
// GPIO_InitStruct.Pin = SOLO_AMS_IRQ_PIN;
// GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT;
// GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
// LL_GPIO_Init(SOLO_AMS_IRQ_PORT, &GPIO_InitStruct);
//
//
// LL_GPIO_SetPinMode(SOLO_AMS_IRQ_PORT,SOLO_AMS_IRQ_PIN,LL_GPIO_MODE_INPUT);
// LL_GPIO_SetPinPull(SOLO_AMS_IRQ_PORT,SOLO_AMS_IRQ_PIN,LL_GPIO_PULL_UP);
#endif
}
/* TIM6 init function */
static void MX_TIM6_Init(void)
void init_millisecond_timer(int lf)
{
LL_TIM_InitTypeDef TIM_InitStruct;
@ -352,7 +876,11 @@ static void MX_TIM6_Init(void)
// 48 MHz sys clock --> 6 MHz timer clock
// 48 MHz / 48000 == 1000 Hz
if (!lf)
TIM_InitStruct.Prescaler = 48000;
else
TIM_InitStruct.Prescaler = MAX_CLOCK_RATE;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 90;
LL_TIM_Init(TIM6, &TIM_InitStruct);
@ -368,39 +896,14 @@ static void MX_TIM6_Init(void)
// Start immediately
LL_TIM_EnableCounter(TIM6);
TIM6->SR = 0;
__enable_irq();
NVIC_EnableIRQ(TIM6_IRQn);
}
/* TIM7 init function */
// static void MX_TIM7_Init(void)
// {
//
// LL_TIM_InitTypeDef TIM_InitStruct;
//
// /* Peripheral clock enable */
// LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM7);
//
// // 48 MHz sys clock --> 6 MHz timer clock
// // 6 MHz / 6000 == 1000 Hz
// TIM_InitStruct.Prescaler = 48000;
// TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
// TIM_InitStruct.Autoreload = 0xffff;
// LL_TIM_Init(TIM6, &TIM_InitStruct);
//
// LL_TIM_DisableARRPreload(TIM7);
//
// LL_TIM_SetTriggerOutput(TIM7, LL_TIM_TRGO_RESET);
//
// LL_TIM_DisableMasterSlaveMode(TIM7);
//
// // enable interrupt
// TIM7->DIER |= 1;
//
// // Start immediately
// LL_TIM_EnableCounter(TIM7);
// }
/* RNG init function */
static void MX_RNG_Init(void)
void init_rng(void)
{
/* Peripheral clock enable */
@ -409,3 +912,45 @@ static void MX_RNG_Init(void)
LL_RNG_Enable(RNG);
}
/* SPI1 init function */
void init_spi(void)
{
LL_SPI_InitTypeDef SPI_InitStruct;
LL_GPIO_InitTypeDef GPIO_InitStruct;
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
/**SPI1 GPIO Configuration
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PA7 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_5|LL_GPIO_PIN_6|LL_GPIO_PIN_7;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* SPI1 parameter configuration*/
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8;
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 7;
LL_SPI_Init(SPI1, &SPI_InitStruct);
LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2018 SoloKeys, Inc. <https://solokeys.com/>
*
* This file is part of Solo.
*
* Solo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Solo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Solo. If not, see <https://www.gnu.org/licenses/>
*
* This code is available under licenses for commercial use.
* Please contact SoloKeys for more information.
*/
#ifndef _INIT_H_
#define _INIT_H_
void init_usb();
void init_gpio(void);
void init_debug_uart(void);
void init_pwm(void);
void init_millisecond_timer(int lf);
void init_rng(void);
void init_spi(void);
#endif

799
targets/stm32l432/src/nfc.c Normal file
View File

@ -0,0 +1,799 @@
#include <string.h>
#include "stm32l4xx.h"
#include "nfc.h"
#include "ams.h"
#include "log.h"
#include "util.h"
#include "device.h"
#include "u2f.h"
#include "crypto.h"
#include "ctap_errors.h"
#define IS_IRQ_ACTIVE() (1 == (LL_GPIO_ReadInputPort(SOLO_AMS_IRQ_PORT) & SOLO_AMS_IRQ_PIN))
// Capability container
const CAPABILITY_CONTAINER NFC_CC = {
.cclen_hi = 0x00, .cclen_lo = 0x0f,
.version = 0x20,
.MLe_hi = 0x00, .MLe_lo = 0x7f,
.MLc_hi = 0x00, .MLc_lo = 0x7f,
.tlv = { 0x04,0x06,
0xe1,0x04,
0x00,0x7f,
0x00,0x00 }
};
// 13 chars
uint8_t NDEF_SAMPLE[] = "\x00\x14\xd1\x01\x0eU\x04solokeys.com/";
// Poor way to get some info while in passive operation
#include <stdarg.h>
void nprintf(const char *format, ...)
{
memmove((char*)NDEF_SAMPLE + sizeof(NDEF_SAMPLE) - 1 - 13," ", 13);
va_list args;
va_start (args, format);
vsnprintf ((char*)NDEF_SAMPLE + sizeof(NDEF_SAMPLE) - 1 - 13, 13, format, args);
va_end (args);
}
static struct
{
uint8_t max_frame_size;
uint8_t cid;
uint8_t block_num;
uint8_t selected_applet;
} NFC_STATE;
void nfc_state_init()
{
memset(&NFC_STATE,0,sizeof(NFC_STATE));
NFC_STATE.max_frame_size = 32;
NFC_STATE.block_num = 1;
}
bool nfc_init()
{
uint32_t t1;
nfc_state_init();
ams_init();
// Detect if we are powered by NFC field by listening for a message for
// first 10 ms.
t1 = millis();
while ((millis() - t1) < 10)
{
if (nfc_loop() > 0)
return 1;
}
// Under USB power. Configure AMS chip.
ams_configure();
return 0;
}
void process_int0(uint8_t int0)
{
}
bool ams_wait_for_tx(uint32_t timeout_ms)
{
uint32_t tstart = millis();
while (tstart + timeout_ms > millis())
{
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
if (int0) process_int0(int0);
if (int0 & AMS_INT_TXE)
return true;
delay(1);
}
return false;
}
bool ams_receive_with_timeout(uint32_t timeout_ms, uint8_t * data, int maxlen, int *dlen)
{
uint8_t buf[32];
*dlen = 0;
uint32_t tstart = millis();
while (tstart + timeout_ms > millis())
{
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
uint8_t buffer_status2 = ams_read_reg(AMS_REG_BUF2);
if (buffer_status2 && (int0 & AMS_INT_RXE))
{
if (buffer_status2 & AMS_BUF_INVALID)
{
printf1(TAG_NFC,"Buffer being updated!\r\n");
}
else
{
uint8_t len = buffer_status2 & AMS_BUF_LEN_MASK;
ams_read_buffer(buf, len);
printf1(TAG_NFC_APDU, ">> ");
dump_hex1(TAG_NFC_APDU, buf, len);
*dlen = MIN(32, MIN(maxlen, len));
memcpy(data, buf, *dlen);
return true;
}
}
delay(1);
}
return false;
}
void nfc_write_frame(uint8_t * data, uint8_t len)
{
if (len > 32)
{
len = 32;
}
ams_write_command(AMS_CMD_CLEAR_BUFFER);
ams_write_buffer(data,len);
ams_write_command(AMS_CMD_TRANSMIT_BUFFER);
printf1(TAG_NFC_APDU, "<< ");
dump_hex1(TAG_NFC_APDU, data, len);
}
bool nfc_write_response_ex(uint8_t req0, uint8_t * data, uint8_t len, uint16_t resp)
{
uint8_t res[32];
if (len > 32 - 3)
return false;
res[0] = NFC_CMD_IBLOCK | (req0 & 3);
if (len && data)
memcpy(&res[1], data, len);
res[len + 1] = resp >> 8;
res[len + 2] = resp & 0xff;
nfc_write_frame(res, 3 + len);
return true;
}
bool nfc_write_response(uint8_t req0, uint16_t resp)
{
return nfc_write_response_ex(req0, NULL, 0, resp);
}
void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
{
uint8_t res[32 + 2];
int sendlen = 0;
uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 3);
if (len <= 31)
{
uint8_t res[32] = {0};
res[0] = iBlock;
if (len && data)
memcpy(&res[1], data, len);
nfc_write_frame(res, len + 1);
} else {
do {
// transmit I block
int vlen = MIN(31, len - sendlen);
res[0] = iBlock;
memcpy(&res[1], &data[sendlen], vlen);
// if not a last block
if (vlen + sendlen < len)
{
res[0] |= 0x10;
}
// send data
nfc_write_frame(res, vlen + 1);
sendlen += vlen;
// wait for transmit (32 bytes aprox 2,5ms)
// if (!ams_wait_for_tx(10))
// {
// printf1(TAG_NFC, "TX timeout. slen: %d \r\n", sendlen);
// break;
// }
// if needs to receive R block (not a last block)
if (res[0] & 0x10)
{
uint8_t recbuf[32] = {0};
int reclen;
if (!ams_receive_with_timeout(100, recbuf, sizeof(recbuf), &reclen))
{
printf1(TAG_NFC, "R block RX timeout %d/%d.\r\n",sendlen,len);
break;
}
if (reclen != 1)
{
printf1(TAG_NFC, "R block length error. len: %d. %d/%d \r\n", reclen,sendlen,len);
dump_hex1(TAG_NFC, recbuf, reclen);
break;
}
if (((recbuf[0] & 0x01) == (res[0] & 1)) && ((recbuf[0] & 0xf6) == 0xa2))
{
printf1(TAG_NFC, "R block error. txdata: %02x rxdata: %02x \r\n", res[0], recbuf[0]);
break;
}
}
iBlock ^= 0x01;
} while (sendlen < len);
}
}
// WTX on/off:
// sends/receives WTX frame to reader every `WTX_time` time in ms
// works via timer interrupts
// WTX: f2 01 91 40 === f2(S-block + WTX, frame without CID) 01(from iso - multiply WTX from ATS by 1) <2b crc16>
static bool WTX_sent;
static bool WTX_fail;
static uint32_t WTX_timer;
bool WTX_process(int read_timeout);
void WTX_clear()
{
WTX_sent = false;
WTX_fail = false;
WTX_timer = 0;
}
bool WTX_on(int WTX_time)
{
WTX_clear();
WTX_timer = millis();
return true;
}
bool WTX_off()
{
WTX_timer = 0;
// read data if we sent WTX
if (WTX_sent)
{
if (!WTX_process(100))
{
printf1(TAG_NFC, "WTX-off get last WTX error\n");
return false;
}
}
if (WTX_fail)
{
printf1(TAG_NFC, "WTX-off fail\n");
return false;
}
WTX_clear();
return true;
}
void WTX_timer_exec()
{
// condition: (timer on) or (not expired[300ms])
if ((WTX_timer <= 0) || WTX_timer + 300 > millis())
return;
WTX_process(10);
WTX_timer = millis();
}
// executes twice a period. 1st for send WTX, 2nd for check the result
// read timeout must be 10 ms to call from interrupt
bool WTX_process(int read_timeout)
{
uint8_t wtx[] = {0xf2, 0x01};
if (WTX_fail)
return false;
if (!WTX_sent)
{
nfc_write_frame(wtx, sizeof(wtx));
WTX_sent = true;
return true;
}
else
{
uint8_t data[32];
int len;
if (!ams_receive_with_timeout(read_timeout, data, sizeof(data), &len))
{
WTX_fail = true;
return false;
}
if (len != 2 || data[0] != 0xf2 || data[1] != 0x01)
{
WTX_fail = true;
return false;
}
WTX_sent = false;
return true;
}
}
int answer_rats(uint8_t parameter)
{
uint8_t fsdi = (parameter & 0xf0) >> 4;
uint8_t cid = (parameter & 0x0f);
NFC_STATE.cid = cid;
if (fsdi == 0)
NFC_STATE.max_frame_size = 16;
else if (fsdi == 1)
NFC_STATE.max_frame_size = 24;
else
NFC_STATE.max_frame_size = 32;
uint8_t res[3 + 11];
res[0] = sizeof(res);
res[1] = 2 | (1<<5); // 2 FSCI == 32 byte frame size, TB is enabled
// frame wait time = (256 * 16 / 13.56MHz) * 2^FWI
// FWI=0, FMT=0.3ms (min)
// FWI=4, FMT=4.8ms (default)
// FWI=10, FMT=309ms
// FWI=12, FMT=1237ms
// FWI=14, FMT=4949ms (max)
res[2] = (12<<4) | (0); // TB (FWI << 4) | (SGTI)
// historical bytes
memcpy(&res[3], (uint8_t *)"SoloKey tap", 11);
nfc_write_frame(res, sizeof(res));
ams_wait_for_tx(10);
return 0;
}
void rblock_acknowledge()
{
uint8_t buf[32];
NFC_STATE.block_num = !NFC_STATE.block_num;
buf[0] = NFC_CMD_RBLOCK | NFC_STATE.block_num;
nfc_write_frame(buf,1);
}
// Selects application. Returns 1 if success, 0 otherwise
int select_applet(uint8_t * aid, int len)
{
if (memcmp(aid,AID_FIDO,sizeof(AID_FIDO)) == 0)
{
NFC_STATE.selected_applet = APP_FIDO;
return APP_FIDO;
}
else if (memcmp(aid,AID_NDEF_TYPE_4,sizeof(AID_NDEF_TYPE_4)) == 0)
{
NFC_STATE.selected_applet = APP_NDEF_TYPE_4;
return APP_NDEF_TYPE_4;
}
else if (memcmp(aid,AID_CAPABILITY_CONTAINER,sizeof(AID_CAPABILITY_CONTAINER)) == 0)
{
NFC_STATE.selected_applet = APP_CAPABILITY_CONTAINER;
return APP_CAPABILITY_CONTAINER;
}
else if (memcmp(aid,AID_NDEF_TAG,sizeof(AID_NDEF_TAG)) == 0)
{
NFC_STATE.selected_applet = APP_NDEF_TAG;
return APP_NDEF_TAG;
}
return APP_NOTHING;
}
void nfc_process_iblock(uint8_t * buf, int len)
{
APDU_HEADER * apdu = (APDU_HEADER *)(buf + 1);
uint8_t * payload = buf + 1 + 5;
uint8_t plen = apdu->lc;
int selected;
CTAP_RESPONSE ctap_resp;
int status;
printf1(TAG_NFC,"Iblock: ");
dump_hex1(TAG_NFC, buf, len);
// TODO this needs to be organized better
switch(apdu->ins)
{
case APDU_INS_SELECT:
if (plen > len - 6)
{
printf1(TAG_ERR, "Truncating APDU length %d\r\n", apdu->lc);
plen = len-6;
}
// if (apdu->p1 == 0 && apdu->p2 == 0x0c)
// {
// printf1(TAG_NFC,"Select NDEF\r\n");
//
// NFC_STATE.selected_applet = APP_NDEF_TAG;
// // Select NDEF file!
// res[0] = NFC_CMD_IBLOCK | (buf[0] & 1);
// res[1] = SW_SUCCESS>>8;
// res[2] = SW_SUCCESS & 0xff;
// nfc_write_frame(res, 3);
// printf1(TAG_NFC,"<< "); dump_hex1(TAG_NFC,res, 3);
// }
// else
{
selected = select_applet(payload, plen);
if (selected == APP_FIDO)
{
// block = buf[0] & 1;
// block = NFC_STATE.block_num;
// block = !block;
// NFC_STATE.block_num = block;
// NFC_STATE.block_num = block;
nfc_write_response_ex(buf[0], (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
printf1(TAG_NFC, "FIDO applet selected.\r\n");
}
else if (selected != APP_NOTHING)
{
nfc_write_response(buf[0], SW_SUCCESS);
printf1(TAG_NFC, "SELECTED %d\r\n", selected);
}
else
{
nfc_write_response(buf[0], SW_FILE_NOT_FOUND);
printf1(TAG_NFC, "NOT selected\r\n"); dump_hex1(TAG_NFC,payload, plen);
}
}
break;
case APDU_FIDO_U2F_VERSION:
if (NFC_STATE.selected_applet != APP_FIDO) {
nfc_write_response(buf[0], SW_INS_INVALID);
break;
}
printf1(TAG_NFC, "U2F GetVersion command.\r\n");
nfc_write_response_ex(buf[0], (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
break;
case APDU_FIDO_U2F_REGISTER:
if (NFC_STATE.selected_applet != APP_FIDO) {
nfc_write_response(buf[0], SW_INS_INVALID);
break;
}
printf1(TAG_NFC, "U2F Register command.\r\n");
if (plen != 64)
{
printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", plen);
nfc_write_response(buf[0], SW_WRONG_LENGTH);
return;
}
timestamp();
// WTX_on(WTX_TIME_DEFAULT);
// SystemClock_Config_LF32();
// delay(300);
device_set_clock_rate(DEVICE_LOW_POWER_FAST);;
u2f_request_nfc(&buf[1], len, &ctap_resp);
device_set_clock_rate(DEVICE_LOW_POWER_IDLE);;
// if (!WTX_off())
// return;
printf1(TAG_NFC,"U2F Register P2 took %d\r\n", timestamp());
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
// printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
printf1(TAG_NFC,"U2F Register answered %d (took %d)\r\n", millis(), timestamp());
break;
case APDU_FIDO_U2F_AUTHENTICATE:
if (NFC_STATE.selected_applet != APP_FIDO) {
nfc_write_response(buf[0], SW_INS_INVALID);
break;
}
printf1(TAG_NFC, "U2F Authenticate command.\r\n");
if (plen != 64 + 1 + buf[6 + 64])
{
delay(5);
printf1(TAG_NFC, "U2F Authenticate request length error. len=%d keyhlen=%d.\r\n", plen, buf[6 + 64]);
nfc_write_response(buf[0], SW_WRONG_LENGTH);
return;
}
timestamp();
// WTX_on(WTX_TIME_DEFAULT);
u2f_request_nfc(&buf[1], len, &ctap_resp);
// if (!WTX_off())
// return;
printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
printf1(TAG_NFC,"U2F Authenticate processing %d (took %d)\r\n", millis(), timestamp());
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
printf1(TAG_NFC,"U2F Authenticate answered %d (took %d)\r\n", millis(), timestamp);
break;
case APDU_FIDO_NFCCTAP_MSG:
if (NFC_STATE.selected_applet != APP_FIDO) {
nfc_write_response(buf[0], SW_INS_INVALID);
break;
}
printf1(TAG_NFC, "FIDO2 CTAP message. %d\r\n", timestamp());
WTX_on(WTX_TIME_DEFAULT);
ctap_response_init(&ctap_resp);
status = ctap_request(payload, plen, &ctap_resp);
if (!WTX_off())
return;
printf1(TAG_NFC, "CTAP resp: 0x%02<30> len: %d\r\n", status, ctap_resp.length);
if (status == CTAP1_ERR_SUCCESS)
{
memmove(&ctap_resp.data[1], &ctap_resp.data[0], ctap_resp.length);
ctap_resp.length += 3;
} else {
ctap_resp.length = 3;
}
ctap_resp.data[0] = status;
ctap_resp.data[ctap_resp.length - 2] = SW_SUCCESS >> 8;
ctap_resp.data[ctap_resp.length - 1] = SW_SUCCESS & 0xff;
printf1(TAG_NFC,"CTAP processing %d (took %d)\r\n", millis(), timestamp());
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
printf1(TAG_NFC,"CTAP answered %d (took %d)\r\n", millis(), timestamp());
break;
case APDU_INS_READ_BINARY:
switch(NFC_STATE.selected_applet)
{
case APP_CAPABILITY_CONTAINER:
printf1(TAG_NFC,"APP_CAPABILITY_CONTAINER\r\n");
if (plen > 15)
{
printf1(TAG_ERR, "Truncating requested CC length %d\r\n", apdu->lc);
plen = 15;
}
nfc_write_response_ex(buf[0], (uint8_t *)&NFC_CC, plen, SW_SUCCESS);
ams_wait_for_tx(10);
break;
case APP_NDEF_TAG:
printf1(TAG_NFC,"APP_NDEF_TAG\r\n");
if (plen > (sizeof(NDEF_SAMPLE) - 1))
{
printf1(TAG_ERR, "Truncating requested CC length %d\r\n", apdu->lc);
plen = sizeof(NDEF_SAMPLE) - 1;
}
nfc_write_response_ex(buf[0], NDEF_SAMPLE, plen, SW_SUCCESS);
ams_wait_for_tx(10);
break;
default:
printf1(TAG_ERR, "No binary applet selected!\r\n");
return;
break;
}
break;
default:
printf1(TAG_NFC, "Unknown INS %02x\r\n", apdu->ins);
nfc_write_response(buf[0], SW_INS_INVALID);
break;
}
}
static uint8_t ibuf[1024];
static int ibuflen = 0;
void clear_ibuf()
{
ibuflen = 0;
memset(ibuf, 0, sizeof(ibuf));
}
void nfc_process_block(uint8_t * buf, unsigned int len)
{
if (!len)
return;
if (IS_PPSS_CMD(buf[0]))
{
printf1(TAG_NFC, "NFC_CMD_PPSS\r\n");
}
else if (IS_IBLOCK(buf[0]))
{
if (buf[0] & 0x10)
{
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining blen=%d len=%d\r\n", ibuflen, len);
if (ibuflen + len > sizeof(ibuf))
{
printf1(TAG_NFC, "I block memory error! must have %d but have only %d\r\n", ibuflen + len, sizeof(ibuf));
nfc_write_response(buf[0], SW_INTERNAL_EXCEPTION);
return;
}
printf1(TAG_NFC_APDU,"i> ");
dump_hex1(TAG_NFC_APDU, buf, len);
if (len)
{
memcpy(&ibuf[ibuflen], &buf[1], len - 1);
ibuflen += len - 1;
}
// send R block
uint8_t rb = NFC_CMD_RBLOCK | NFC_CMD_RBLOCK_ACK | (buf[0] & 3);
nfc_write_frame(&rb, 1);
} else {
if (ibuflen)
{
if (len)
{
memcpy(&ibuf[ibuflen], &buf[1], len - 1);
ibuflen += len - 1;
}
memmove(&ibuf[1], ibuf, ibuflen);
ibuf[0] = buf[0];
ibuflen++;
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining last block. blen=%d len=%d\r\n", ibuflen, len);
printf1(TAG_NFC_APDU,"i> ");
dump_hex1(TAG_NFC_APDU, buf, len);
nfc_process_iblock(ibuf, ibuflen);
} else {
// printf1(TAG_NFC, "NFC_CMD_IBLOCK\r\n");
nfc_process_iblock(buf, len);
}
clear_ibuf();
}
}
else if (IS_RBLOCK(buf[0]))
{
rblock_acknowledge();
printf1(TAG_NFC, "NFC_CMD_RBLOCK\r\n");
}
else if (IS_SBLOCK(buf[0]))
{
if ((buf[0] & NFC_SBLOCK_DESELECT) == 0)
{
printf1(TAG_NFC, "NFC_CMD_SBLOCK, DESELECTED\r\n");
nfc_write_frame(buf, 1);
ams_wait_for_tx(2);
ams_write_command(AMS_CMD_SLEEP);
nfc_state_init();
clear_ibuf();
WTX_clear();
}
else
{
printf1(TAG_NFC, "NFC_CMD_SBLOCK, Unknown. len[%d]\r\n", len);
}
dump_hex1(TAG_NFC, buf, len);
}
else
{
printf1(TAG_NFC, "unknown NFC request\r\n len[%d]:", len);
dump_hex1(TAG_NFC, buf, len);
}
}
int nfc_loop()
{
uint8_t buf[32];
AMS_DEVICE ams;
int len = 0;
read_reg_block(&ams);
uint8_t state = AMS_STATE_MASK & ams.regs.rfid_status;
if (state != AMS_STATE_SELECTED && state != AMS_STATE_SELECTEDX)
{
// delay(1); // sleep ?
return 0;
}
if (ams.regs.rfid_status)
{
// if (state != AMS_STATE_SENSE)
// printf1(TAG_NFC," %s x%02x\r\n", ams_get_state_string(ams.regs.rfid_status), state);
}
if (ams.regs.int0 & AMS_INT_INIT)
{
nfc_state_init();
}
if (ams.regs.int1)
{
// ams_print_int1(ams.regs.int1);
}
if ((ams.regs.int0 & AMS_INT_RXE))
{
if (ams.regs.buffer_status2)
{
if (ams.regs.buffer_status2 & AMS_BUF_INVALID)
{
printf1(TAG_NFC,"Buffer being updated!\r\n");
}
else
{
len = ams.regs.buffer_status2 & AMS_BUF_LEN_MASK;
ams_read_buffer(buf, len);
}
}
}
if (len)
{
// ISO 14443-3
switch(buf[0])
{
case NFC_CMD_REQA:
printf1(TAG_NFC, "NFC_CMD_REQA\r\n");
break;
case NFC_CMD_WUPA:
printf1(TAG_NFC, "NFC_CMD_WUPA\r\n");
break;
case NFC_CMD_HLTA:
printf1(TAG_NFC, "HLTA/Halt\r\n");
break;
case NFC_CMD_RATS:
answer_rats(buf[1]);
NFC_STATE.block_num = 1;
clear_ibuf();
WTX_clear();
break;
default:
// ISO 14443-4
nfc_process_block(buf,len);
break;
}
}
return len;
}

View File

@ -0,0 +1,64 @@
#ifndef _NFC_H_
#define _NFC_H_
#include <stdint.h>
#include <stdbool.h>
#include "apdu.h"
// Return number of bytes read if any.
int nfc_loop();
bool nfc_init();
typedef struct
{
uint8_t cclen_hi;
uint8_t cclen_lo;
uint8_t version;
uint8_t MLe_hi;
uint8_t MLe_lo;
uint8_t MLc_hi;
uint8_t MLc_lo;
uint8_t tlv[8];
} __attribute__((packed)) CAPABILITY_CONTAINER;
// WTX time in ms
#define WTX_TIME_DEFAULT 300
#define NFC_CMD_REQA 0x26
#define NFC_CMD_WUPA 0x52
#define NFC_CMD_HLTA 0x50
#define NFC_CMD_RATS 0xe0
#define NFC_CMD_PPSS 0xd0
#define IS_PPSS_CMD(x) (((x) & 0xf0) == NFC_CMD_PPSS)
#define NFC_CMD_IBLOCK 0x00
#define IS_IBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_IBLOCK) && (((x) & 0x02) == 0x02) )
#define NFC_CMD_RBLOCK 0x80
#define NFC_CMD_RBLOCK_ACK 0x20
#define IS_RBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_RBLOCK) && (((x) & 0x02) == 0x02) )
#define NFC_CMD_SBLOCK 0xc0
#define IS_SBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_SBLOCK) && (((x) & 0x02) == 0x02) )
#define NFC_SBLOCK_DESELECT 0x30
#define NFC_SBLOCK_WTX 0x30
#define AID_NDEF_TYPE_4 "\xD2\x76\x00\x00\x85\x01\x01"
#define AID_NDEF_MIFARE_TYPE_4 "\xD2\x76\x00\x00\x85\x01\x00"
#define AID_CAPABILITY_CONTAINER "\xE1\x03"
#define AID_NDEF_TAG "\xE1\x04"
#define AID_FIDO "\xa0\x00\x00\x06\x47\x2f\x00\x01"
typedef enum
{
APP_NOTHING = 0,
APP_NDEF_TYPE_4 = 1,
APP_MIFARE_TYPE_4,
APP_CAPABILITY_CONTAINER,
APP_NDEF_TAG,
APP_FIDO,
} APPLETS;
void WTX_timer_exec();
#endif

View File

@ -24,19 +24,33 @@ void _putchar(char c)
}
int _write (int fd, const void *buf, long int len)
int _write (int fd, const void *buf, unsigned long int len)
{
uint8_t * data = (uint8_t *) buf;
#if DEBUG_LEVEL>1
// static uint8_t logbuf[1000] = {0};
// static int logbuflen = 0;
// if (logbuflen + len > sizeof(logbuf)) {
// int mlen = logbuflen + len - sizeof(logbuf);
// memmove(logbuf, &logbuf[mlen], sizeof(logbuf) - mlen);
// logbuflen -= mlen;
// }
// memcpy(&logbuf[logbuflen], data, len);
// logbuflen += len;
// Send out USB serial
CDC_Transmit_FS(data, len);
// if (res == USBD_OK)
// logbuflen = 0;
#endif
#ifdef ENABLE_SERIAL_PRINTING
// Send out UART serial
while(len--)
{
_putchar(*data++);
}
#endif
return 0;
}
#endif

View File

@ -17,7 +17,7 @@ int __errno = 0;
void rng_get_bytes(uint8_t * dst, size_t sz)
{
uint8_t r[8];
uint8_t r[4];
unsigned int i,j;
for (i = 0; i < sz; i += 4)
{
@ -33,7 +33,7 @@ void rng_get_bytes(uint8_t * dst, size_t sz)
for (j = 0; j < 4; j++)
{
if ((i + j) > sz)
if ((i + j) >= sz)
{
return;
}

View File

@ -1,183 +0,0 @@
/*
*****************************************************************************
**
** File : LinkerScript.ld
**
** Abstract : Linker script for STM32L432KCUx Device with
** 256KByte FLASH, 64KByte RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
**
** Distribution: The file is distributed as is, without any warranty
** of any kind.
**
** (c)Copyright Ac6.
** You may use this file as-is or modify it according to the needs of your
** project. Distribution of this file (unmodified or modified) is not
** permitted. Ac6 permit registered System Workbench for MCU users the
** rights to distribute the assembled, compiled & linked contents of this
** file as part of an application binary file, provided that it is built
** using the System Workbench for MCU toolchain.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x20010000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 256K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(8);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(8);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(8);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(8);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(8);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(8);
} >FLASH
.ARM.extab :
{
. = ALIGN(8);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(8);
} >FLASH
.ARM : {
. = ALIGN(8);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(8);
} >FLASH
.preinit_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(8);
} >FLASH
.init_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(8);
} >FLASH
.fini_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}

View File

@ -79,6 +79,8 @@ Reset_Handler:
ldr sp, =_estack /* Atollic update: set stack pointer */
/* Copy the data segment initializers from flash to SRAM */
/* Call the clock system intitialization function.*/
bl SystemInit
movs r1, #0
b LoopCopyDataInit
@ -106,8 +108,7 @@ LoopFillZerobss:
cmp r2, r3
bcc FillZerobss
/* Call the clock system intitialization function.*/
bl SystemInit
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/

View File

@ -106,6 +106,8 @@
*/
#include "stm32l4xx.h"
#include "device.h"
#include "init.h"
#if !defined (HSE_VALUE)
#define HSE_VALUE 8000000U /*!< Value of the External oscillator in Hz */
@ -219,6 +221,9 @@ void SystemInit(void)
/* Disable all interrupts */
RCC->CIER = 0x00000000U;
// TODO this is causing boot issues for old bootloader
device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
}
/**

View File

@ -1,834 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2019 SoloKeys Developers
#
# Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
# http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
# http://opensource.org/licenses/MIT>, at your option. This file may not be
# copied, modified, or distributed except according to those terms.
#
# Script for testing correctness of CTAP2/CTAP1 security token
from __future__ import print_function, absolute_import, unicode_literals
from fido2.hid import CtapHidDevice, CTAPHID
from fido2.client import Fido2Client, ClientError
from fido2.ctap import CtapError
from fido2.ctap1 import CTAP1
from fido2.ctap2 import *
from fido2.cose import *
from fido2.utils import Timeout, sha256
import sys, os, time
from random import randint
from binascii import hexlify
import array, struct, socket
# Set up a FIDO 2 client using the origin https://example.com
def ForceU2F(client, device):
client.ctap = CTAP1(device)
client.pin_protocol = None
client._do_make_credential = client._ctap1_make_credential
client._do_get_assertion = client._ctap1_get_assertion
class Packet(object):
def __init__(self, data):
l = len(data)
self.data = data
def ToWireFormat(self,):
return self.data
@staticmethod
def FromWireFormat(pkt_size, data):
return Packet(data)
class Tester:
def __init__(self,):
self.origin = "https://examplo.org"
self.host = "examplo.org"
def find_device(self,):
print(list(CtapHidDevice.list_devices()))
dev = next(CtapHidDevice.list_devices(), None)
if not dev:
raise RuntimeError("No FIDO device found")
self.dev = dev
self.client = Fido2Client(dev, self.origin)
self.ctap = self.client.ctap2
self.ctap1 = CTAP1(dev)
# consume timeout error
# cmd,resp = self.recv_raw()
def send_data(self, cmd, data):
if type(data) != type(b""):
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
with Timeout(1.0) as event:
return self.dev.call(cmd, data, event)
def send_raw(self, data, cid=None):
if cid is None:
cid = self.dev._dev.cid
elif type(cid) != type(b""):
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
if type(data) != type(b""):
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
data = cid + data
l = len(data)
if l != 64:
pad = "\x00" * (64 - l)
pad = struct.pack("%dB" % len(pad), *[ord(x) for x in pad])
data = data + pad
data = list(data)
assert len(data) == 64
self.dev._dev.InternalSendPacket(Packet(data))
def cid(self,):
return self.dev._dev.cid
def set_cid(self, cid):
if type(cid) not in [type(b""), type(bytearray())]:
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
self.dev._dev.cid = cid
def recv_raw(self,):
with Timeout(1.0) as t:
cmd, payload = self.dev._dev.InternalRecv()
return cmd, payload
def check_error(self, data, err=None):
assert len(data) == 1
if err is None:
if data[0] != 0:
raise CtapError(data[0])
elif data[0] != err:
raise ValueError("Unexpected error: %02x" % data[0])
def test_long_ping(self):
amt = 1000
pingdata = os.urandom(amt)
try:
t1 = time.time() * 1000
r = self.send_data(CTAPHID.PING, pingdata)
t2 = time.time() * 1000
delt = t2 - t1
# if (delt < 140 ):
# raise RuntimeError('Fob is too fast (%d ms)' % delt)
if delt > 555 * (amt / 1000):
raise RuntimeError("Fob is too slow (%d ms)" % delt)
if r != pingdata:
raise ValueError("Ping data not echo'd")
print("1000 byte ping time: %s ms" % delt)
except CtapError as e:
print("7609 byte Ping failed:", e)
raise RuntimeError("ping failed")
print("PASS: 7609 byte ping")
# sys.flush(sys.sto)
sys.stdout.flush()
def test_hid(self, check_timeouts=False):
if check_timeouts:
print("Test idle")
try:
cmd, resp = self.recv_raw()
except socket.timeout:
print("Pass: Idle")
print("Test init")
r = self.send_data(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11")
pingdata = os.urandom(100)
try:
r = self.send_data(CTAPHID.PING, pingdata)
if r != pingdata:
raise ValueError("Ping data not echo'd")
except CtapError as e:
print("100 byte Ping failed:", e)
raise RuntimeError("ping failed")
print("PASS: 100 byte ping")
self.test_long_ping()
try:
r = self.send_data(CTAPHID.WINK, "")
print(hexlify(r))
# assert(len(r) == 0)
except CtapError as e:
print("wink failed:", e)
raise RuntimeError("wink failed")
print("PASS: wink")
# try:
# r = self.send_data(CTAPHID.WINK, 'we9gofrei8g')
# raise RuntimeError('Wink is not supposed to have payload')
# except CtapError as e:
# assert(e.code == CtapError.ERR.INVALID_LENGTH)
# print('PASS: malformed wink')
try:
r = self.send_data(CTAPHID.CBOR, "")
if len(r) > 1 or r[0] == 0:
raise RuntimeError("Cbor is supposed to have payload")
except CtapError as e:
assert e.code == CtapError.ERR.INVALID_LENGTH
print("PASS: no data cbor")
try:
r = self.send_data(CTAPHID.MSG, "")
print(hexlify(r))
if len(r) > 2:
raise RuntimeError("MSG is supposed to have payload")
except CtapError as e:
assert e.code == CtapError.ERR.INVALID_LENGTH
print("PASS: no data msg")
try:
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
except CtapError as e:
raise RuntimeError("resync fail: ", e)
print("PASS: resync")
try:
r = self.send_data(0x66, "")
raise RuntimeError("Invalid command did not return error")
except CtapError as e:
assert e.code == CtapError.ERR.INVALID_COMMAND
print("PASS: invalid HID command")
print("Sending packet with too large of a length.")
self.send_raw("\x81\x1d\xba\x00")
cmd, resp = self.recv_raw()
self.check_error(resp, CtapError.ERR.INVALID_LENGTH)
print("PASS: invalid length")
r = self.send_data(CTAPHID.PING, "\x44" * 200)
print("Sending packets that skip a sequence number.")
self.send_raw("\x81\x04\x90")
self.send_raw("\x00")
self.send_raw("\x01")
# skip 2
self.send_raw("\x03")
cmd, resp = self.recv_raw()
self.check_error(resp, CtapError.ERR.INVALID_SEQ)
if check_timeouts:
cmd, resp = self.recv_raw()
assert cmd == 0xBF # timeout
print("PASS: invalid sequence")
print("Resync and send ping")
try:
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
pingdata = os.urandom(100)
r = self.send_data(CTAPHID.PING, pingdata)
if r != pingdata:
raise ValueError("Ping data not echo'd")
except CtapError as e:
raise RuntimeError("resync fail: ", e)
print("PASS: resync and ping")
print("Send ping and abort it")
self.send_raw("\x81\x04\x00")
self.send_raw("\x00")
self.send_raw("\x01")
try:
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
except CtapError as e:
raise RuntimeError("resync fail: ", e)
print("PASS: interrupt ping with resync")
print("Send ping and abort it with different cid, expect timeout")
oldcid = self.cid()
newcid = "\x11\x22\x33\x44"
self.send_raw("\x81\x10\x00")
self.send_raw("\x00")
self.send_raw("\x01")
self.set_cid(newcid)
self.send_raw(
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88"
) # init from different cid
print("wait for init response")
cmd, r = self.recv_raw() # init response
assert cmd == 0x86
self.set_cid(oldcid)
if check_timeouts:
# print('wait for timeout')
cmd, r = self.recv_raw() # timeout response
assert cmd == 0xBF
print("PASS: resync and timeout")
print("Test timeout")
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
t1 = time.time() * 1000
self.send_raw("\x81\x04\x00")
self.send_raw("\x00")
self.send_raw("\x01")
cmd, r = self.recv_raw() # timeout response
t2 = time.time() * 1000
delt = t2 - t1
assert cmd == 0xBF
assert r[0] == CtapError.ERR.TIMEOUT
assert delt < 1000 and delt > 400
print("Pass timeout")
print("Test not cont")
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
self.send_raw("\x81\x04\x00")
self.send_raw("\x00")
self.send_raw("\x01")
self.send_raw("\x81\x10\x00") # init packet
cmd, r = self.recv_raw() # timeout response
assert cmd == 0xBF
assert r[0] == CtapError.ERR.INVALID_SEQ
print("PASS: Test not cont")
if check_timeouts:
print("Check random cont ignored")
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
self.send_raw("\x01\x10\x00")
try:
cmd, r = self.recv_raw() # timeout response
except socket.timeout:
pass
print("PASS: random cont")
print("Check busy")
t1 = time.time() * 1000
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
oldcid = self.cid()
newcid = "\x11\x22\x33\x44"
self.send_raw("\x81\x04\x00")
self.set_cid(newcid)
self.send_raw("\x81\x04\x00")
cmd, r = self.recv_raw() # busy response
t2 = time.time() * 1000
assert t2 - t1 < 100
assert cmd == 0xBF
assert r[0] == CtapError.ERR.CHANNEL_BUSY
self.set_cid(oldcid)
cmd, r = self.recv_raw() # timeout response
assert cmd == 0xBF
assert r[0] == CtapError.ERR.TIMEOUT
print("PASS: busy")
print("Check busy interleaved")
cid1 = "\x11\x22\x33\x44"
cid2 = "\x01\x22\x33\x44"
self.set_cid(cid2)
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
self.set_cid(cid1)
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
self.send_raw("\x81\x00\x63") # echo 99 bytes first channel
self.set_cid(cid2) # send ping on 2nd channel
self.send_raw("\x81\x00\x63")
self.send_raw("\x00")
cmd, r = self.recv_raw() # busy response
self.set_cid(cid1) # finish 1st channel ping
self.send_raw("\x00")
self.set_cid(cid2)
assert cmd == 0xBF
assert r[0] == CtapError.ERR.CHANNEL_BUSY
self.set_cid(cid1)
cmd, r = self.recv_raw() # ping response
assert cmd == 0x81
assert len(r) == 0x63
if check_timeouts:
cmd, r = self.recv_raw() # timeout
assert cmd == 0xBF
assert r[0] == CtapError.ERR.TIMEOUT
print("PASS: busy interleaved")
if check_timeouts:
print("Test idle, wait for timeout")
sys.stdout.flush()
try:
cmd, resp = self.recv_raw()
except socket.timeout:
print("Pass: Idle")
print("Test cid 0 is invalid")
self.set_cid("\x00\x00\x00\x00")
self.send_raw(
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\x00\x00\x00\x00"
)
cmd, r = self.recv_raw() # timeout
assert cmd == 0xBF
assert r[0] == CtapError.ERR.INVALID_CHANNEL
print("Pass: cid 0")
print("Test invalid broadcast cid use")
self.set_cid("\xff\xff\xff\xff")
self.send_raw(
"\x81\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\xff\xff\xff\xff"
)
cmd, r = self.recv_raw() # timeout
assert cmd == 0xBF
assert r[0] == CtapError.ERR.INVALID_CHANNEL
print("Pass: cid broadcast")
def test_u2f(self,):
chal = sha256(b"AAA")
appid = sha256(b"BBB")
for i in range(0, 5):
reg = self.ctap1.register(chal, appid)
reg.verify(appid, chal)
auth = self.ctap1.authenticate(chal, appid, reg.key_handle)
# check endianness
assert auth.counter < 0x10000
print("U2F reg + auth pass %d/5" % (i + 1))
def test_fido2_simple(self, pin_token=None):
creds = []
exclude_list = []
rp = {"id": self.host, "name": "ExaRP"}
user = {"id": b"usee_od", "name": "AB User"}
challenge = "Y2hhbGxlbmdl"
PIN = pin_token
fake_id1 = array.array("B", [randint(0, 255) for i in range(0, 150)]).tobytes()
fake_id2 = array.array("B", [randint(0, 255) for i in range(0, 73)]).tobytes()
exclude_list.append({"id": fake_id1, "type": "public-key"})
exclude_list.append({"id": fake_id2, "type": "public-key"})
print("MC")
t1 = time.time() * 1000
attest, data = self.client.make_credential(
rp, user, challenge, pin=PIN, exclude_list=[]
)
t2 = time.time() * 1000
attest.verify(data.hash)
print("Register valid (%d ms)" % (t2 - t1))
cred = attest.auth_data.credential_data
creds.append(cred)
allow_list = [{"id": creds[0].credential_id, "type": "public-key"}]
t1 = time.time() * 1000
assertions, client_data = self.client.get_assertion(
rp["id"], challenge, allow_list, pin=PIN
)
t2 = time.time() * 1000
assertions[0].verify(client_data.hash, creds[0].public_key)
print("Assertion valid (%d ms)" % (t2 - t1))
def test_fido2_brute_force(self):
creds = []
exclude_list = []
rp = {"id": self.host, "name": "ExaRP"}
user = {"id": b"usee_od", "name": "AB User"}
PIN = None
abc = "abcdefghijklnmopqrstuvwxyz"
abc += abc.upper()
self.ctap.reset()
for i in range(0, 2048 ** 2):
creds = []
challenge = "".join([abc[randint(0, len(abc) - 1)] for x in range(0, 32)])
fake_id1 = array.array(
"B", [randint(0, 255) for i in range(0, 150)]
).tostring()
fake_id2 = array.array(
"B", [randint(0, 255) for i in range(0, 73)]
).tostring()
exclude_list.append({"id": fake_id1, "type": "public-key"})
exclude_list.append({"id": fake_id2, "type": "public-key"})
# for i in range(0,2048**2):
for i in range(0, 1):
t1 = time.time() * 1000
attest, data = self.client.make_credential(
rp, user, challenge, pin=PIN, exclude_list=[]
)
print(attest.auth_data.counter)
t2 = time.time() * 1000
attest.verify(data.hash)
print("Register valid (%d ms)" % (t2 - t1))
sys.stdout.flush()
cred = attest.auth_data.credential_data
creds.append(cred)
# for i in range(0,2048**2):
for i in range(0, 1):
allow_list = [{"id": creds[0].credential_id, "type": "public-key"}]
t1 = time.time() * 1000
assertions, client_data = self.client.get_assertion(
rp["id"], challenge, allow_list, pin=PIN
)
t2 = time.time() * 1000
assertions[0].verify(client_data.hash, creds[0].public_key)
print(assertions[0].auth_data.counter)
print("Assertion valid (%d ms)" % (t2 - t1))
sys.stdout.flush()
def test_fido2(self):
def test(self, pincode=None):
creds = []
exclude_list = []
rp = {"id": self.host, "name": "ExaRP"}
user = {"id": b"usee_od", "name": "AB User"}
challenge = "Y2hhbGxlbmdl"
PIN = pincode
fake_id1 = array.array(
"B", [randint(0, 255) for i in range(0, 150)]
).tostring()
fake_id2 = array.array(
"B", [randint(0, 255) for i in range(0, 73)]
).tostring()
exclude_list.append({"id": fake_id1, "type": "public-key"})
exclude_list.append({"id": fake_id2, "type": "public-key"})
# test make credential
print("make 3 credentials")
for i in range(0, 3):
attest, data = self.client.make_credential(
rp, user, challenge, pin=PIN, exclude_list=[]
)
attest.verify(data.hash)
# verify endian-ness is correct
assert attest.auth_data.counter < 0x10000
cred = attest.auth_data.credential_data
creds.append(cred)
print(cred)
print("PASS")
if PIN is not None:
print("make credential with wrong pin code")
try:
attest, data = self.client.make_credential(
rp, user, challenge, pin=PIN + " ", exclude_list=[]
)
except CtapError as e:
assert e.code == CtapError.ERR.PIN_INVALID
except ClientError as e:
assert e.cause.code == CtapError.ERR.PIN_INVALID
print("PASS")
print("make credential with exclude list")
attest, data = self.client.make_credential(
rp, user, challenge, pin=PIN, exclude_list=exclude_list
)
attest.verify(data.hash)
cred = attest.auth_data.credential_data
creds.append(cred)
print("PASS")
print("make credential with exclude list including real credential")
real_excl = [{"id": cred.credential_id, "type": "public-key"}]
try:
attest, data = self.client.make_credential(
rp, user, challenge, pin=PIN, exclude_list=exclude_list + real_excl
)
raise RuntimeError("Exclude list did not return expected error")
except CtapError as e:
assert e.code == CtapError.ERR.CREDENTIAL_EXCLUDED
except ClientError as e:
assert e.cause.code == CtapError.ERR.CREDENTIAL_EXCLUDED
print("PASS")
for i, x in enumerate(creds):
print("get assertion %d" % i)
allow_list = [{"id": x.credential_id, "type": "public-key"}]
assertions, client_data = self.client.get_assertion(
rp["id"], challenge, allow_list, pin=PIN
)
assertions[0].verify(client_data.hash, x.public_key)
print("PASS")
if PIN is not None:
print("get assertion with wrong pin code")
try:
assertions, client_data = self.client.get_assertion(
rp["id"], challenge, allow_list, pin=PIN + " "
)
except CtapError as e:
assert e.code == CtapError.ERR.PIN_INVALID
except ClientError as e:
assert e.cause.code == CtapError.ERR.PIN_INVALID
print("PASS")
print("get multiple assertions")
allow_list = [{"id": x.credential_id, "type": "public-key"} for x in creds]
assertions, client_data = self.client.get_assertion(
rp["id"], challenge, allow_list, pin=PIN
)
for ass, cred in zip(assertions, creds):
i += 1
ass.verify(client_data.hash, cred.public_key)
print("%d verified" % i)
print("PASS")
print("Reset device")
try:
self.ctap.reset()
except CtapError as e:
print("Warning, reset failed: ", e)
pass
print("PASS")
test(self, None)
print("Set a pin code")
PIN = "1122aabbwfg0h9g !@#=="
self.client.pin_protocol.set_pin(PIN)
print("PASS")
print("Illegally set pin code again")
try:
self.client.pin_protocol.set_pin(PIN)
except CtapError as e:
assert e.code == CtapError.ERR.NOT_ALLOWED
print("PASS")
print("Change pin code")
PIN2 = PIN + "_pin2"
self.client.pin_protocol.change_pin(PIN, PIN2)
PIN = PIN2
print("PASS")
print("Change pin code using wrong pin")
try:
self.client.pin_protocol.change_pin(PIN.replace("a", "b"), "1234")
except CtapError as e:
assert e.code == CtapError.ERR.PIN_INVALID
print("PASS")
print("MC using wrong pin")
try:
self.test_fido2_simple("abcd3")
except ClientError as e:
assert e.cause.code == CtapError.ERR.PIN_INVALID
print("PASS")
print("get info")
inf = self.ctap.get_info()
print("PASS")
self.test_fido2_simple(PIN)
print("Re-run make_credential and get_assertion tests with pin code")
test(self, PIN)
print("Reset device")
try:
self.ctap.reset()
except CtapError as e:
print("Warning, reset failed: ", e)
print("PASS")
def test_rk(self,):
creds = []
rp = {"id": self.host, "name": "ExaRP"}
user0 = {"id": b"first one", "name": "single User"}
users = [
{"id": b"user" + os.urandom(16), "name": "AB User"} for i in range(0, 2)
]
challenge = "Y2hhbGxlbmdl"
PIN = None
print("reset")
self.ctap.reset()
# if PIN: self.client.pin_protocol.set_pin(PIN)
print("registering 1 user with RK")
t1 = time.time() * 1000
attest, data = self.client.make_credential(
rp, user0, challenge, pin=PIN, exclude_list=[], rk=True
)
t2 = time.time() * 1000
attest.verify(data.hash)
creds.append(attest.auth_data.credential_data)
print("Register valid (%d ms)" % (t2 - t1))
print("1 assertion")
t1 = time.time() * 1000
assertions, client_data = self.client.get_assertion(
rp["id"], challenge, pin=PIN
)
t2 = time.time() * 1000
assertions[0].verify(client_data.hash, creds[0].public_key)
print("Assertion valid (%d ms)" % (t2 - t1))
print(assertions[0], client_data)
print("registering %d users with RK" % len(users))
for i in range(0, len(users)):
t1 = time.time() * 1000
attest, data = self.client.make_credential(
rp, users[i], challenge, pin=PIN, exclude_list=[], rk=True
)
t2 = time.time() * 1000
attest.verify(data.hash)
print("Register valid (%d ms)" % (t2 - t1))
creds.append(attest.auth_data.credential_data)
t1 = time.time() * 1000
assertions, client_data = self.client.get_assertion(
rp["id"], challenge, pin=PIN
)
t2 = time.time() * 1000
for x, y in zip(assertions, creds):
x.verify(client_data.hash, y.public_key)
print("Assertion(s) valid (%d ms)" % (t2 - t1))
print("registering a duplicate user ")
t1 = time.time() * 1000
attest, data = self.client.make_credential(
rp, users[1], challenge, pin=PIN, exclude_list=[], rk=True
)
t2 = time.time() * 1000
attest.verify(data.hash)
creds = creds[:2] + creds[3:] + [attest.auth_data.credential_data]
print("Register valid (%d ms)" % (t2 - t1))
t1 = time.time() * 1000
assertions, client_data = self.client.get_assertion(
rp["id"], challenge, pin=PIN
)
t2 = time.time() * 1000
assert len(assertions) == len(users) + 1
for x, y in zip(assertions, creds):
x.verify(client_data.hash, y.public_key)
print("Assertion(s) valid (%d ms)" % (t2 - t1))
def test_responses(self,):
PIN = "1234"
RPID = self.host
for dev in CtapHidDevice.list_devices():
print("dev", dev)
client = Fido2Client(dev, RPID)
ctap = client.ctap2
# ctap.reset()
try:
if PIN:
client.pin_protocol.set_pin(PIN)
except:
pass
inf = ctap.get_info()
# print (inf)
print("versions: ", inf.versions)
print("aaguid: ", inf.aaguid)
print("rk: ", inf.options["rk"])
print("clientPin: ", inf.options["clientPin"])
print("max_message_size: ", inf.max_msg_size)
# rp = {'id': 'SelectDevice', 'name': 'SelectDevice'}
rp = {"id": RPID, "name": "ExaRP"}
user = {"id": os.urandom(10), "name": "SelectDevice"}
user = {"id": b"21first one", "name": "single User"}
challenge = "Y2hhbGxlbmdl"
if 1:
attest, data = client.make_credential(
rp, user, challenge, exclude_list=[], pin=PIN, rk=True
)
cred = attest.auth_data.credential_data
creds = [cred]
allow_list = [{"id": creds[0].credential_id, "type": "public-key"}]
allow_list = []
assertions, client_data = client.get_assertion(
rp["id"], challenge, pin=PIN
)
assertions[0].verify(client_data.hash, creds[0].public_key)
if 0:
print("registering 1 user with RK")
t1 = time.time() * 1000
attest, data = client.make_credential(
rp, user, challenge, pin=PIN, exclude_list=[], rk=True
)
t2 = time.time() * 1000
attest.verify(data.hash)
creds = [attest.auth_data.credential_data]
print("Register valid (%d ms)" % (t2 - t1))
print("1 assertion")
t1 = time.time() * 1000
assertions, client_data = client.get_assertion(
rp["id"], challenge, pin=PIN
)
t2 = time.time() * 1000
assertions[0].verify(client_data.hash, creds[0].public_key)
print("Assertion valid (%d ms)" % (t2 - t1))
# print('fmt:',attest.fmt)
# print('rp_id_hash',attest.auth_data.rp_id_hash)
# print('flags:', hex(attest.auth_data.flags))
# print('count:', hex(attest.auth_data.counter))
print("flags MC:", attest.auth_data)
print("flags GA:", assertions[0].auth_data)
# print('cred_id:',attest.auth_data.credential_data.credential_id)
# print('pubkey:',attest.auth_data.credential_data.public_key)
# print('aaguid:',attest.auth_data.credential_data.aaguid)
# print('cred data:',attest.auth_data.credential_data)
# print('auth_data:',attest.auth_data)
# print('auth_data:',attest.auth_data)
# print('alg:',attest.att_statement['alg'])
# print('sig:',attest.att_statement['sig'])
# print('x5c:',attest.att_statement['x5c'])
# print('data:',data)
print("assertion:", assertions[0])
print("clientData:", client_data)
print()
# break
def test_find_brute_force():
i = 0
while 1:
t1 = time.time() * 1000
t = Tester()
t.find_device()
t2 = time.time() * 1000
print("connected %d (%d ms)" % (i, t2 - t1))
i += 1
time.sleep(0.01)
if __name__ == "__main__":
t = Tester()
t.find_device()
# t.test_hid()
# t.test_long_ping()
t.test_fido2()
t.test_u2f()
# t.test_rk()
# t.test_responses()
# test_find_brute_force()
# t.test_fido2_simple()
# t.test_fido2_brute_force()

View File

@ -1,5 +1,7 @@
ecdsa
fido2
intelhex
pyserial
fido2
solo-python
pyusb
wheel

File diff suppressed because it is too large Load Diff

8
tools/test_sw_token.sh Normal file
View File

@ -0,0 +1,8 @@
#!/bin/bash
./main
while [ $? == 100 ] ; do
echo "Restarting software authentictor."
./main
done

58
tools/testing/main.py Normal file
View File

@ -0,0 +1,58 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2019 SoloKeys Developers
#
# Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
# http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
# http://opensource.org/licenses/MIT>, at your option. This file may not be
# copied, modified, or distributed except according to those terms.
#
# Script for testing correctness of CTAP2/CTAP1 security token
import sys
from solo.fido2 import force_udp_backend
from tests import Tester, FIDO2Tests, U2FTests, HIDTests, SoloTests
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: %s [sim] <[u2f]|[fido2]|[rk]|[hid]|[ping]>")
sys.exit(0)
t = Tester()
t.set_user_count(3)
if "sim" in sys.argv:
print("Using UDP backend.")
force_udp_backend()
t.set_sim(True)
t.set_user_count(10)
t.find_device()
if "solo" in sys.argv:
SoloTests(t).run()
if "u2f" in sys.argv:
U2FTests(t).run()
if "fido2" in sys.argv:
# t.test_fido2()
FIDO2Tests(t).run()
# hid tests are a bit invasive and should be done last
if "hid" in sys.argv:
HIDTests(t).run()
if "bootloader" in sys.argv:
if t.is_sim:
raise RuntimeError("Cannot test bootloader in simulation yet.")
# print("Put device in bootloader mode and then hit enter")
# input()
# t.test_bootloader()
# t.test_responses()
# t.test_fido2_brute_force()

View File

@ -0,0 +1,11 @@
from . import fido2
from . import hid
from . import solo
from . import u2f
from . import tester
FIDO2Tests = fido2.FIDO2Tests
HIDTests = hid.HIDTests
U2FTests = u2f.U2FTests
SoloTests = solo.SoloTests
Tester = tester.Tester

1099
tools/testing/tests/fido2.py Normal file

File diff suppressed because it is too large Load Diff

252
tools/testing/tests/hid.py Normal file
View File

@ -0,0 +1,252 @@
import sys, os, time
from binascii import hexlify
from fido2.hid import CTAPHID
from fido2.ctap import CtapError
from .tester import Tester, Test
class HIDTests(Tester):
def __init__(self, tester=None):
super().__init__(tester)
self.check_timeouts = False
def set_check_timeouts(self, en):
self.check_timeouts = en
def run(self,):
self.test_long_ping()
self.test_hid(self.check_timeouts)
def test_long_ping(self):
amt = 1000
pingdata = os.urandom(amt)
with Test("Send %d byte ping" % amt):
try:
t1 = time.time() * 1000
r = self.send_data(CTAPHID.PING, pingdata)
t2 = time.time() * 1000
delt = t2 - t1
# if (delt < 140 ):
# raise RuntimeError('Fob is too fast (%d ms)' % delt)
if delt > 555 * (amt / 1000):
raise RuntimeError("Fob is too slow (%d ms)" % delt)
if r != pingdata:
raise ValueError("Ping data not echo'd")
except CtapError:
raise RuntimeError("ping failed")
sys.stdout.flush()
def test_hid(self, check_timeouts=False):
if check_timeouts:
with Test("idle"):
try:
cmd, resp = self.recv_raw()
except socket.timeout:
pass
with Test("init"):
r = self.send_data(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11")
with Test("100 byte ping"):
pingdata = os.urandom(100)
try:
r = self.send_data(CTAPHID.PING, pingdata)
if r != pingdata:
raise ValueError("Ping data not echo'd")
except CtapError as e:
print("100 byte Ping failed:", e)
raise RuntimeError("ping failed")
self.test_long_ping()
with Test("Wink"):
r = self.send_data(CTAPHID.WINK, "")
with Test("CBOR msg with no data"):
try:
r = self.send_data(CTAPHID.CBOR, "")
if len(r) > 1 or r[0] == 0:
raise RuntimeError("Cbor is supposed to have payload")
except CtapError as e:
assert e.code == CtapError.ERR.INVALID_LENGTH
with Test("No data in U2F msg"):
try:
r = self.send_data(CTAPHID.MSG, "")
print(hexlify(r))
if len(r) > 2:
raise RuntimeError("MSG is supposed to have payload")
except CtapError as e:
assert e.code == CtapError.ERR.INVALID_LENGTH
with Test("Use init command to resync"):
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
with Test("Invalid HID command"):
try:
r = self.send_data(0x66, "")
raise RuntimeError("Invalid command did not return error")
except CtapError as e:
assert e.code == CtapError.ERR.INVALID_COMMAND
with Test("Sending packet with too large of a length."):
self.send_raw("\x81\x1d\xba\x00")
cmd, resp = self.recv_raw()
Tester.check_error(resp, CtapError.ERR.INVALID_LENGTH)
r = self.send_data(CTAPHID.PING, "\x44" * 200)
with Test("Sending packets that skip a sequence number."):
self.send_raw("\x81\x04\x90")
self.send_raw("\x00")
self.send_raw("\x01")
# skip 2
self.send_raw("\x03")
cmd, resp = self.recv_raw()
Tester.check_error(resp, CtapError.ERR.INVALID_SEQ)
with Test("Resync and send ping"):
try:
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
pingdata = os.urandom(100)
r = self.send_data(CTAPHID.PING, pingdata)
if r != pingdata:
raise ValueError("Ping data not echo'd")
except CtapError as e:
raise RuntimeError("resync fail: ", e)
with Test("Send ping and abort it"):
self.send_raw("\x81\x04\x00")
self.send_raw("\x00")
self.send_raw("\x01")
try:
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
except CtapError as e:
raise RuntimeError("resync fail: ", e)
with Test("Send ping and abort it with different cid, expect timeout"):
oldcid = self.cid()
newcid = "\x11\x22\x33\x44"
self.send_raw("\x81\x10\x00")
self.send_raw("\x00")
self.send_raw("\x01")
self.set_cid(newcid)
self.send_raw(
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88"
) # init from different cid
print("wait for init response")
cmd, r = self.recv_raw() # init response
assert cmd == 0x86
self.set_cid(oldcid)
if check_timeouts:
# print('wait for timeout')
cmd, r = self.recv_raw() # timeout response
assert cmd == 0xBF
with Test("Test timeout"):
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
t1 = time.time() * 1000
self.send_raw("\x81\x04\x00")
self.send_raw("\x00")
self.send_raw("\x01")
cmd, r = self.recv_raw() # timeout response
t2 = time.time() * 1000
delt = t2 - t1
assert cmd == 0xBF
assert r[0] == CtapError.ERR.TIMEOUT
assert delt < 1000 and delt > 400
with Test("Test not cont"):
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
self.send_raw("\x81\x04\x00")
self.send_raw("\x00")
self.send_raw("\x01")
self.send_raw("\x81\x10\x00") # init packet
cmd, r = self.recv_raw() # timeout response
assert cmd == 0xBF
assert r[0] == CtapError.ERR.INVALID_SEQ
if check_timeouts:
with Test("Check random cont ignored"):
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
self.send_raw("\x01\x10\x00")
try:
cmd, r = self.recv_raw() # timeout response
except socket.timeout:
pass
with Test("Check busy"):
t1 = time.time() * 1000
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
oldcid = self.cid()
newcid = "\x11\x22\x33\x44"
self.send_raw("\x81\x04\x00")
self.set_cid(newcid)
self.send_raw("\x81\x04\x00")
cmd, r = self.recv_raw() # busy response
t2 = time.time() * 1000
assert t2 - t1 < 100
assert cmd == 0xBF
assert r[0] == CtapError.ERR.CHANNEL_BUSY
self.set_cid(oldcid)
cmd, r = self.recv_raw() # timeout response
assert cmd == 0xBF
assert r[0] == CtapError.ERR.TIMEOUT
with Test("Check busy interleaved"):
cid1 = "\x11\x22\x33\x44"
cid2 = "\x01\x22\x33\x44"
self.set_cid(cid2)
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
self.set_cid(cid1)
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
self.send_raw("\x81\x00\x63") # echo 99 bytes first channel
self.set_cid(cid2) # send ping on 2nd channel
self.send_raw("\x81\x00\x63")
Tester.delay(0.1)
self.send_raw("\x00")
cmd, r = self.recv_raw() # busy response
self.set_cid(cid1) # finish 1st channel ping
self.send_raw("\x00")
self.set_cid(cid2)
assert cmd == 0xBF
assert r[0] == CtapError.ERR.CHANNEL_BUSY
self.set_cid(cid1)
cmd, r = self.recv_raw() # ping response
assert cmd == 0x81
assert len(r) == 0x63
if check_timeouts:
with Test("Test idle, wait for timeout"):
sys.stdout.flush()
try:
cmd, resp = self.recv_raw()
except socket.timeout:
pass
with Test("Test cid 0 is invalid"):
self.set_cid("\x00\x00\x00\x00")
self.send_raw(
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\x00\x00\x00\x00"
)
cmd, r = self.recv_raw() # timeout
assert cmd == 0xBF
assert r[0] == CtapError.ERR.INVALID_CHANNEL
with Test("Test invalid broadcast cid use"):
self.set_cid("\xff\xff\xff\xff")
self.send_raw(
"\x81\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\xff\xff\xff\xff"
)
cmd, r = self.recv_raw() # timeout
assert cmd == 0xBF
assert r[0] == CtapError.ERR.INVALID_CHANNEL

View File

@ -0,0 +1,70 @@
from solo.client import SoloClient
from fido2.ctap1 import ApduError
from .util import shannon_entropy
from .tester import Tester, Test
class SoloTests(Tester):
def __init__(self, tester=None):
super().__init__(tester)
def run(self,):
self.test_solo()
def test_solo(self,):
"""
Solo specific tests
"""
# RNG command
sc = SoloClient()
sc.find_device(self.dev)
sc.use_u2f()
memmap = (0x08005000, 0x08005000 + 198 * 1024 - 8)
total = 1024 * 16
with Test("Gathering %d random bytes..." % total):
entropy = b""
while len(entropy) < total:
entropy += sc.get_rng()
with Test("Test entropy is close to perfect"):
s = shannon_entropy(entropy)
assert s > 7.98
print("Entropy is %.5f bits per byte." % s)
with Test("Test Solo version command"):
assert len(sc.solo_version()) == 3
with Test("Test bootloader is not active"):
try:
sc.write_flash(memmap[0], b"1234")
except ApduError:
pass
sc.exchange = sc.exchange_fido2
with Test("Test Solo version and random commands with fido2 layer"):
assert len(sc.solo_version()) == 3
sc.get_rng()
def test_bootloader(self,):
sc = SoloClient()
sc.find_device(self.dev)
sc.use_u2f()
memmap = (0x08005000, 0x08005000 + 198 * 1024 - 8)
data = b"A" * 64
with Test("Test version command"):
assert len(sc.bootloader_version()) == 3
with Test("Test write command"):
sc.write_flash(memmap[0], data)
for addr in (memmap[0] - 8, memmap[0] - 4, memmap[1], memmap[1] - 8):
with Test("Test out of bounds write command at 0x%04x" % addr):
try:
sc.write_flash(addr, data)
except CtapError as e:
assert e.code == CtapError.ERR.NOT_ALLOWED

View File

@ -0,0 +1,197 @@
import time, struct
from fido2.hid import CtapHidDevice
from fido2.client import Fido2Client
from fido2.ctap1 import CTAP1
from fido2.utils import Timeout
from fido2.ctap import CtapError
def ForceU2F(client, device):
client.ctap = CTAP1(device)
client.pin_protocol = None
client._do_make_credential = client._ctap1_make_credential
client._do_get_assertion = client._ctap1_get_assertion
class Packet(object):
def __init__(self, data):
self.data = data
def ToWireFormat(self,):
return self.data
@staticmethod
def FromWireFormat(pkt_size, data):
return Packet(data)
class Test:
def __init__(self, msg):
self.msg = msg
def __enter__(self,):
print(self.msg)
def __exit__(self, a, b, c):
print("Pass")
class Tester:
def __init__(self, tester=None):
self.origin = "https://examplo.org"
self.host = "examplo.org"
self.user_count = 10
self.is_sim = False
if tester:
self.initFromTester(tester)
def initFromTester(self, tester):
self.user_count = tester.user_count
self.is_sim = tester.is_sim
self.dev = tester.dev
self.ctap = tester.ctap
self.ctap1 = tester.ctap1
self.client = tester.client
def find_device(self,):
print(list(CtapHidDevice.list_devices()))
dev = next(CtapHidDevice.list_devices(), None)
if not dev:
raise RuntimeError("No FIDO device found")
self.dev = dev
self.client = Fido2Client(dev, self.origin)
self.ctap = self.client.ctap2
self.ctap1 = CTAP1(dev)
# consume timeout error
# cmd,resp = self.recv_raw()
def set_user_count(self, count):
self.user_count = count
def set_sim(self, b):
self.is_sim = b
def reboot(self,):
if self.is_sim:
print("Sending restart command...")
self.send_magic_reboot()
Tester.delay(0.25)
else:
print("Please reboot authentictor and hit enter")
input()
self.find_device()
def send_data(self, cmd, data):
if not isinstance(data, bytes):
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
with Timeout(1.0) as event:
return self.dev.call(cmd, data, event)
def send_raw(self, data, cid=None):
if cid is None:
cid = self.dev._dev.cid
elif not isinstance(cid, bytes):
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
if not isinstance(data, bytes):
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
data = cid + data
l = len(data)
if l != 64:
pad = "\x00" * (64 - l)
pad = struct.pack("%dB" % len(pad), *[ord(x) for x in pad])
data = data + pad
data = list(data)
assert len(data) == 64
self.dev._dev.InternalSendPacket(Packet(data))
def send_magic_reboot(self,):
"""
For use in simulation and testing. Random bytes that authentictor should detect
and then restart itself.
"""
magic_cmd = (
b"\xac\x10\x52\xca\x95\xe5\x69\xde\x69\xe0\x2e\xbf"
+ b"\xf3\x33\x48\x5f\x13\xf9\xb2\xda\x34\xc5\xa8\xa3"
+ b"\x40\x52\x66\x97\xa9\xab\x2e\x0b\x39\x4d\x8d\x04"
+ b"\x97\x3c\x13\x40\x05\xbe\x1a\x01\x40\xbf\xf6\x04"
+ b"\x5b\xb2\x6e\xb7\x7a\x73\xea\xa4\x78\x13\xf6\xb4"
+ b"\x9a\x72\x50\xdc"
)
self.dev._dev.InternalSendPacket(Packet(magic_cmd))
def cid(self,):
return self.dev._dev.cid
def set_cid(self, cid):
if not isinstance(cid, (bytes, bytearray)):
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
self.dev._dev.cid = cid
def recv_raw(self,):
with Timeout(1.0):
cmd, payload = self.dev._dev.InternalRecv()
return cmd, payload
def check_error(data, err=None):
assert len(data) == 1
if err is None:
if data[0] != 0:
raise CtapError(data[0])
elif data[0] != err:
raise ValueError("Unexpected error: %02x" % data[0])
def testFunc(self, func, test, *args, **kwargs):
with Test(test):
res = None
expectedError = kwargs.get("expectedError", None)
otherArgs = kwargs.get("other", {})
try:
res = func(*args, **otherArgs)
if expectedError != CtapError.ERR.SUCCESS:
raise RuntimeError("Expected error to occur for test: %s" % test)
except CtapError as e:
if expectedError is not None:
cond = e.code != expectedError
if isinstance(expectedError, list):
cond = e.code not in expectedError
else:
expectedError = [expectedError]
if cond:
raise RuntimeError(
f"Got error code {hex(e.code)}, expected {[hex(x) for x in expectedError]}"
)
else:
print(e)
return res
def testReset(self,):
print("Resetting Authenticator...")
try:
self.ctap.reset()
except CtapError:
# Some authenticators need a power cycle
print("You must power cycle authentictor. Hit enter when done.")
input()
time.sleep(0.2)
self.find_device()
self.ctap.reset()
def testMC(self, test, *args, **kwargs):
return self.testFunc(self.ctap.make_credential, test, *args, **kwargs)
def testGA(self, test, *args, **kwargs):
return self.testFunc(self.ctap.get_assertion, test, *args, **kwargs)
def testCP(self, test, *args, **kwargs):
return self.testFunc(self.ctap.client_pin, test, *args, **kwargs)
def testPP(self, test, *args, **kwargs):
return self.testFunc(
self.client.pin_protocol.get_pin_token, test, *args, **kwargs
)
def delay(secs):
time.sleep(secs)

121
tools/testing/tests/u2f.py Normal file
View File

@ -0,0 +1,121 @@
from fido2.ctap1 import CTAP1, ApduError, APDU
from fido2.utils import sha256
from fido2.client import _call_polling
from .tester import Tester, Test
class U2FTests(Tester):
def __init__(self, tester=None):
super().__init__(tester)
def run(self,):
self.test_u2f()
def register(self, chal, appid):
reg_data = _call_polling(0.25, None, None, self.ctap1.register, chal, appid)
return reg_data
def authenticate(self, chal, appid, key_handle, check_only=False):
auth_data = _call_polling(
0.25,
None,
None,
self.ctap1.authenticate,
chal,
appid,
key_handle,
check_only=check_only,
)
return auth_data
def test_u2f(self,):
chal = sha256(b"AAA")
appid = sha256(b"BBB")
lastc = 0
regs = []
with Test("Check version"):
assert self.ctap1.get_version() == "U2F_V2"
with Test("Check bad INS"):
try:
self.ctap1.send_apdu(0, 0, 0, 0, b"")
except ApduError as e:
assert e.code == 0x6D00
with Test("Check bad CLA"):
try:
self.ctap1.send_apdu(1, CTAP1.INS.VERSION, 0, 0, b"abc")
except ApduError as e:
assert e.code == 0x6E00
for i in range(0, self.user_count):
with Test(
"U2F reg + auth %d/%d (count: %02x)" % (i + 1, self.user_count, lastc)
):
reg = self.register(chal, appid)
reg.verify(appid, chal)
auth = self.authenticate(chal, appid, reg.key_handle)
auth.verify(appid, chal, reg.public_key)
regs.append(reg)
# check endianness
if lastc:
assert (auth.counter - lastc) < 10
lastc = auth.counter
if lastc > 0x80000000:
print("WARNING: counter is unusually high: %04x" % lastc)
assert 0
for i in range(0, self.user_count):
with Test(
"Checking previous registration %d/%d" % (i + 1, self.user_count)
):
auth = self.authenticate(chal, appid, regs[i].key_handle)
auth.verify(appid, chal, regs[i].public_key)
print("Check that all previous credentials are registered...")
for i in range(0, self.user_count):
with Test("Check that previous credential %d is registered" % i):
try:
auth = self.ctap1.authenticate(
chal, appid, regs[i].key_handle, check_only=True
)
except ApduError as e:
# Indicates that key handle is registered
assert e.code == APDU.USE_NOT_SATISFIED
with Test("Check an incorrect key handle is not registered"):
kh = bytearray(regs[0].key_handle)
kh[0] = kh[0] ^ (0x40)
try:
self.ctap1.authenticate(chal, appid, kh, check_only=True)
assert 0
except ApduError as e:
assert e.code == APDU.WRONG_DATA
with Test("Try to sign with incorrect key handle"):
try:
self.ctap1.authenticate(chal, appid, kh)
assert 0
except ApduError as e:
assert e.code == APDU.WRONG_DATA
with Test("Try to sign using an incorrect keyhandle length"):
try:
kh = regs[0].key_handle
self.ctap1.authenticate(chal, appid, kh[: len(kh) // 2])
assert 0
except ApduError as e:
assert e.code == APDU.WRONG_DATA
with Test("Try to sign using an incorrect appid"):
badid = bytearray(appid)
badid[0] = badid[0] ^ (0x40)
try:
auth = self.ctap1.authenticate(chal, badid, regs[0].key_handle)
assert 0
except ApduError as e:
assert e.code == APDU.WRONG_DATA

View File

@ -0,0 +1,12 @@
import math
def shannon_entropy(data):
s = 0.0
total = len(data)
for x in range(0, 256):
freq = data.count(x)
p = freq / total
if p > 0:
s -= p * math.log2(p)
return s

View File

@ -0,0 +1,19 @@
# Notify ModemManager this device should be ignored
ACTION!="add|change|move", GOTO="mm_usb_device_blacklist_end"
SUBSYSTEM!="usb", GOTO="mm_usb_device_blacklist_end"
ENV{DEVTYPE}!="usb_device", GOTO="mm_usb_device_blacklist_end"
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ENV{ID_MM_DEVICE_IGNORE}="1"
LABEL="mm_usb_device_blacklist_end"
# Solo bootloader + firmware access
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess"
# ST DFU access
SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", TAG+="uaccess"
# U2F Zero
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess"

View File

@ -0,0 +1,19 @@
# Notify ModemManager this device should be ignored
ACTION!="add|change|move", GOTO="mm_usb_device_blacklist_end"
SUBSYSTEM!="usb", GOTO="mm_usb_device_blacklist_end"
ENV{DEVTYPE}!="usb_device", GOTO="mm_usb_device_blacklist_end"
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ENV{ID_MM_DEVICE_IGNORE}="1"
LABEL="mm_usb_device_blacklist_end"
# Solo bootloader + firmware access
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", MODE="0660", GROUP="plugdev"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", MODE="0660", GROUP="plugdev"
# ST DFU access
SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="0660", GROUP="plugdev"
# U2F Zero
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", MODE="0660", GROUP="plugdev"

View File

@ -0,0 +1,17 @@
# TODO: would like to lookup ID_SERIAL_SHORT from `usb` SUBSYSTEM
# but link on `hidraw` subsystem level
# and end up with symlinks `/dev/solo[hacker|secure]-<serial>`
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", SYMLINK+="solo-$env{ID_SERIAL_SHORT}-%n"
## Solo Secure symlinks
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ATTRS{product}=="Solo [1-9]*", SYMLINK+="solosecure-$env{ID_SERIAL_SHORT}-%n"
## Solo Hacker symlinks
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ATTRS{product}=="Solo Hacker [1-9]*", SYMLINK+="solohacker-$env{ID_SERIAL_SHORT}-%n"
## Solo Serial access + symlink
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", SYMLINK+="soloserial"
# Non-unique rules (breakdown if multiple Solos are plugged in)
## Solo
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", SYMLINK+="solo"
## U2F Zero
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", SYMLINK+="u2fzero"

30
udev/Makefile Normal file
View File

@ -0,0 +1,30 @@
# On modern systems, udev has a TAG uaccess, which is used in 73-seat-late.rules
# On older systems, we use GROUP plugdev with MODE
# --> Try `make setup` first, if it doesn't work, try `make legacy-setup`.
#
# The symlinks are optional, install with `make symlinks`.
#
# We keep 99-solo.rules in the parent directory but deprecate it,
# remove when documentation is updated.
setup: install activate
legacy-setup: install-legacy activate
# Symlinks can be setup, we don't officially supply any
# symlinks: install-symlinks activate
RULES_PATH=/etc/udev/rules.d
activate:
sudo udevadm control --reload-rules
sudo udevadm trigger
install:
sudo cp $(PWD)/70-solokeys-access.rules ${RULES_PATH}/70-solokeys-access.rules
install-legacy:
sudo cp $(PWD)/70-solokeys-legacy-access.rules ${RULES_PATH}/70-solokeys-access.rules
# install-symlinks:
# sudo cp $(PWD)/71-solokeys-symlinks.rules ${RULES_PATH}/71-solokeys-symlinks.rules

14
udev/README.md Normal file
View File

@ -0,0 +1,14 @@
This is for Linux systems only.
To install the official SoloKeys udev rules, allowing access to your key, run
```
make install
```
This should work assuming your system is reasonably up-to-date. If not, try
```
make install-legacy
```