Compare commits

...

251 Commits

Author SHA1 Message Date
434c155a25 docs: update .all-contributorsrc 2019-08-29 20:44:40 +00:00
893898c5e7 docs: update README.md 2019-08-29 20:44:39 +00:00
83dd92d9ba Update STABLE_VERSION 2019-08-29 22:05:10 +08:00
5d3914bc5e remove delays 2019-08-23 22:25:22 +08:00
abe306a649 Merge branch 'master' of github.com:solokeys/solo 2019-08-23 14:53:22 +08:00
41ceb78f6c add user presence to flags 2019-08-23 14:48:21 +08:00
8e192f2363 do not delay bootloader 2019-08-23 14:41:26 +08:00
affc256ca2 add delay to cap button improve reliability 2019-08-23 14:41:26 +08:00
b3ac739a35 make touch sensor edge based to avoid approving >1 transaction 2019-08-23 13:44:06 +08:00
3b53537077 refactor fido2 user presence handling & increase timeout to 29s 2019-08-23 13:19:28 +08:00
3fad9a7a7d add response to reset command and delete debug 2019-08-23 10:43:09 +08:00
8973608f59 docs: update .all-contributorsrc 2019-08-22 22:42:17 +02:00
8af6505f6d docs: update README.md 2019-08-22 22:42:17 +02:00
d39d7978fd small fix 2019-08-22 21:04:01 +08:00
c972a13034 fix reboot 2019-08-22 20:55:25 +08:00
a95e62e2ea reset 2019-08-22 20:55:25 +08:00
c79b7abfb6 add reset placeholder 2019-08-22 20:55:25 +08:00
dfb124dc8b refactoring 2019-08-22 20:55:12 +08:00
972760eb78 added APDU input chaining 2019-08-22 20:55:12 +08:00
0d621d13f9 fix decoding apdu 2019-08-22 20:55:12 +08:00
728acc1671 chaining not needs to go to the start 2019-08-21 12:13:16 +08:00
62b4418dac fix pck length math 2019-08-21 12:13:16 +08:00
8059a9765f was wrong buffer 2019-08-21 12:13:16 +08:00
b743d5fac5 sketch 2019-08-21 12:13:16 +08:00
dccfb0d1b3 stub pc build 2019-08-21 12:06:06 +08:00
a72f0ede05 take a lazy approach to key agreement generation to not hold up boot time for nfc 2019-08-21 12:06:06 +08:00
adcbd3aeb8 speed up public key derivation slightly for nfc 2019-08-21 12:06:06 +08:00
d931954a13 remove WTX, move debug log 2019-08-21 12:06:06 +08:00
b706cc30b0 for now, always gen key agreement 2019-08-21 12:06:06 +08:00
57fe39704b Merge pull request #282 from solokeys/update-udev-docs
Update udev docs
2019-08-21 02:48:33 +02:00
4b6619b705 Update udev docs 2019-08-21 02:37:15 +02:00
095b08e3d9 add some stability in small responses 2019-08-19 22:33:32 +08:00
89e021003a small fix for HID readers 2019-08-19 22:33:32 +08:00
4f3d4b09eb Update README.md 2019-08-16 17:22:46 -04:00
3f4843b03a Merge pull request #270 from solokeys/bump_2.4.3
Update STABLE_VERSION
2019-08-16 21:07:59 +02:00
26af0c423e Update solo-extras.md 2019-08-16 14:04:43 +08:00
19422d9daa add info for rng use 2019-08-16 14:04:43 +08:00
b7a4cf001a run through fixes 2019-08-16 14:04:43 +08:00
3927aec06d dont remove solo.hex bootloader.hex 2019-08-16 14:04:43 +08:00
f5794481ae initial draft 2019-08-16 14:04:43 +08:00
caac9d0cc1 add secure build that uses default attestation key 2019-08-16 14:04:43 +08:00
ffadab05a3 Update STABLE_VERSION 2019-08-15 19:35:54 +08:00
2423154fee fix warning 2019-08-15 18:07:40 +08:00
cf79b7865d small fix 2019-08-15 17:50:16 +08:00
6f0cf99c92 PPS implementation 2019-08-15 17:50:16 +08:00
7ef68fd5d3 Merge pull request #265 from solokeys/fix_cdc_interfaces
Fix cdc interfaces
2019-08-15 17:49:53 +08:00
3be8611fcf remove duplicate from merge 2019-08-15 17:44:09 +08:00
21489658a7 Merge branch 'master' into fix_cdc_interfaces 2019-08-15 17:38:57 +08:00
a07a3dee8d refactor user_presence handling 2019-08-15 17:36:35 +08:00
416da63a9a not for bootloader 2019-08-15 17:36:35 +08:00
027fa791a3 only 1 user presence auth per button press 2019-08-15 17:36:35 +08:00
3e52d7b42b cache button press for 2s 2019-08-15 17:36:35 +08:00
301e18c6a2 add some int0 logic to main cycle 2019-08-14 14:32:03 +08:00
44205141eb add one place for int0 2019-08-14 14:32:03 +08:00
6e1110ca9b fix bug with ams_wait_for_tx 2019-08-14 14:32:03 +08:00
9105b988e2 fix some warnings 2019-08-14 14:32:03 +08:00
14c94ea8f5 minor typo 2019-08-14 14:26:45 +08:00
435b908c17 Merge pull request #241 from Wesseldr/feature/STM32L432_documentation_update
Added OsX arm install, updated FIDO2 test site links
2019-08-14 14:23:41 +08:00
78280e570b adjust whitespace 2019-08-12 16:18:47 +08:00
36aec9f20b separate interface into two and add "IAD" descriptor 2019-08-12 16:18:30 +08:00
b5d3276df6 not for bootloader 2019-08-11 18:16:58 +08:00
ffd854a303 only 1 user presence auth per button press 2019-08-11 18:05:08 +08:00
349a84dcec cache button press for 2s 2019-08-11 17:59:31 +08:00
6c6a9bc5b6 Merge pull request #260 from solokeys/all-contributors/add-szszszsz
docs: add szszszsz as a contributor
2019-08-08 01:14:36 +02:00
e1528cb248 docs: update .all-contributorsrc 2019-08-07 23:14:18 +00:00
e0955c190e docs: update README.md 2019-08-07 23:14:17 +00:00
ef1e9bc1a1 Merge pull request #259 from solokeys/all-contributors/add-hughsie
docs: add hughsie as a contributor
2019-08-08 01:05:29 +02:00
f6c7dbdbdd docs: update .all-contributorsrc 2019-08-07 23:05:02 +00:00
ea1969ca94 docs: update README.md 2019-08-07 23:05:01 +00:00
c14bc76ddf Merge pull request #258 from solokeys/all-contributors/add-m3hm00d
docs: add m3hm00d as a contributor
2019-08-08 00:59:52 +02:00
f7fd30b9ae docs: update .all-contributorsrc 2019-08-07 22:59:27 +00:00
11763b6502 docs: update README.md 2019-08-07 22:59:26 +00:00
ae5d341c72 Merge pull request #255 from solokeys/all-contributors/add-rgerganov
docs: add rgerganov as a contributor
2019-08-08 00:57:23 +02:00
afed99bae9 Merge branch 'master' into all-contributors/add-rgerganov 2019-08-08 00:57:16 +02:00
d6078ce015 Merge pull request #257 from solokeys/all-contributors/add-esden
docs: add esden as a contributor
2019-08-08 00:55:01 +02:00
d630254224 docs: update .all-contributorsrc 2019-08-07 22:53:47 +00:00
b2ac7b5479 docs: update README.md 2019-08-07 22:53:46 +00:00
c050b82210 editing fail 2019-08-08 00:53:23 +02:00
ba8119b370 Merge pull request #256 from solokeys/all-contributors/add-manuel-domke
docs: add manuel-domke as a contributor
2019-08-08 00:51:24 +02:00
d350f9a638 Update .all-contributorsrc 2019-08-08 00:50:53 +02:00
53275e0182 docs: update .all-contributorsrc 2019-08-07 22:49:29 +00:00
2e14ac5b11 docs: update README.md 2019-08-07 22:49:28 +00:00
a760bed29f docs: update .all-contributorsrc 2019-08-07 22:45:57 +00:00
e854184a11 docs: update README.md 2019-08-07 22:45:56 +00:00
87c2076107 Merge pull request #254 from solokeys/all-contributors/add-alphathegeek
docs: add alphathegeek as a contributor
2019-08-08 00:44:46 +02:00
9be192b82f docs: update .all-contributorsrc 2019-08-07 22:44:17 +00:00
e95233bb5f docs: update README.md 2019-08-07 22:44:16 +00:00
2a2145b7f2 Merge pull request #253 from solokeys/all-contributors/add-StoyanDimitrov
docs: add StoyanDimitrov as a contributor
2019-08-08 00:43:09 +02:00
7cfd0ccb19 docs: update .all-contributorsrc 2019-08-07 22:42:57 +00:00
7d84caa754 docs: update README.md 2019-08-07 22:42:56 +00:00
f7dd595d84 Merge pull request #252 from solokeys/all-contributors/add-yparitcher
docs: add yparitcher as a contributor
2019-08-08 00:42:34 +02:00
04570aaf15 docs: update .all-contributorsrc 2019-08-07 22:42:17 +00:00
1a3aad9761 docs: update README.md 2019-08-07 22:42:16 +00:00
9545dacc5d Merge pull request #251 from solokeys/all-contributors/add-pjz
docs: add pjz as a contributor
2019-08-08 00:41:00 +02:00
b5e592adc9 docs: update .all-contributorsrc 2019-08-07 22:40:47 +00:00
1cd6692246 docs: update README.md 2019-08-07 22:40:46 +00:00
17884505b4 Merge pull request #250 from solokeys/all-contributors/add-YakBizzarro
docs: add YakBizzarro as a contributor
2019-08-08 00:38:06 +02:00
c2312fa69a docs: update .all-contributorsrc 2019-08-07 22:37:54 +00:00
832f06b6b4 docs: update README.md 2019-08-07 22:37:53 +00:00
4ab5477f96 Merge pull request #249 from solokeys/all-contributors/add-ehershey
docs: add ehershey as a contributor
2019-08-08 00:36:13 +02:00
434f2f89ec docs: update .all-contributorsrc 2019-08-07 22:35:54 +00:00
9174c00abf docs: update README.md 2019-08-07 22:35:53 +00:00
46111e27e3 Merge pull request #248 from solokeys/all-contributors/add-dschuermann
docs: add dschuermann as a contributor
2019-08-08 00:33:51 +02:00
763995763f docs: update .all-contributorsrc 2019-08-07 22:33:40 +00:00
4f317863e9 docs: update README.md 2019-08-07 22:33:39 +00:00
8f9aeebb9b Merge pull request #247 from solokeys/all-contributors/add-aseigler
docs: add aseigler as a contributor
2019-08-08 00:32:18 +02:00
86c6b3cbc8 docs: update .all-contributorsrc 2019-08-07 22:32:05 +00:00
a53dfb2812 docs: update README.md 2019-08-07 22:32:05 +00:00
04d6cffcb6 Merge pull request #246 from solokeys/all-contributors/add-agl
docs: add agl as a contributor
2019-08-08 00:29:03 +02:00
76e46605e3 Merge branch 'master' into all-contributors/add-agl 2019-08-08 00:28:36 +02:00
dc94e73c62 Merge pull request #245 from solokeys/all-contributors/add-merlokk
docs: add merlokk as a contributor
2019-08-08 00:26:09 +02:00
018311cfab docs: update .all-contributorsrc 2019-08-07 22:24:54 +00:00
c37638d4cb docs: update README.md 2019-08-07 22:24:53 +00:00
81a67eda24 docs: update .all-contributorsrc 2019-08-07 22:23:26 +00:00
70c176e9ef docs: update README.md 2019-08-07 22:23:25 +00:00
7e39425e98 Merge pull request #243 from solokeys/all-contributors/add-Wesseldr
docs: add Wesseldr as a contributor
2019-08-07 23:59:00 +02:00
25bf9d8e54 docs: update .all-contributorsrc 2019-08-07 21:58:17 +00:00
2e67a5f772 docs: update README.md 2019-08-07 21:58:16 +00:00
66befb96b8 Clarify that code contributions are assumed to be dual-licensed 2019-08-07 23:55:44 +02:00
257f6d1ed5 Merge pull request #242 from solokeys/all-contributors/add-szszszsz
docs: add szszszsz as a contributor
2019-08-07 23:54:17 +02:00
b4b59a6d4d Merge all-contributors with existing Contributors section 2019-08-07 23:52:52 +02:00
c18a08b14b docs: create .all-contributorsrc 2019-08-07 21:26:37 +00:00
c249148d80 docs: update README.md 2019-08-07 21:26:36 +00:00
02a51454b7 Added OsX arm install, updated FIDO2 test site links 2019-08-07 18:35:41 +02:00
4e3420f19f Merge pull request #236 from Wesseldr/patch-3
Cleaned nucleo32-board.md
2019-08-06 13:48:00 +02:00
6b8e575fac Merge branch 'master' into patch-3 2019-08-06 13:43:13 +02:00
28788d1eeb Merge pull request #237 from solokeys/remove_tests
Remove tests
2019-08-06 13:22:29 +02:00
1ee031ae9c Merge pull request #65 from Nitrokey/docs-nucleo32-board
Describe running Solo on the Nucleo board
2019-08-06 13:20:19 +02:00
6dfe42e486 All cleaned up now ? 2019-08-06 13:11:51 +02:00
29b2032dae fixing final-definitions and misc 2019-08-06 13:06:06 +02:00
f9f1e96c73 Update README.md 2019-08-06 18:54:57 +08:00
e2738d11d3 remove tests 2019-08-06 18:50:05 +08:00
a8c7c43e14 Cleaned nucleo32-board.md
The original pull request #65 was not completely compliant. hopefully this one is.
2019-08-06 12:40:05 +02:00
db479850a6 OsX path for STM32_Programmer_CLI
Added how to add the STM32_Programmer_CLI to your OsX path

Fixes https://github.com/Nitrokey/nitrokey-fido2-firmware/pull/20
2019-08-05 16:06:20 +02:00
09d450ed02 Little typo
Fixes https://github.com/Nitrokey/nitrokey-fido2-firmware/pull/19
2019-08-05 16:04:19 +02:00
be37ed46f7 Add instruction for manual flashing of the Nucleo board
Signed-off-by: Szczepan Zalega <szczepan@nitrokey.com>
2019-08-05 16:00:30 +02:00
420d052ac9 Describe running Solo on the Nucleo32 board
Signed-off-by: Szczepan Zalega <szczepan@nitrokey.com>
2019-08-05 16:00:27 +02:00
3698c942a2 Merge pull request #233 from solokeys/bump
Update STABLE_VERSION
2019-07-29 22:40:20 +02:00
17a170bb90 Update STABLE_VERSION 2019-07-29 14:58:30 -04:00
d4e61421b6 Merge pull request #232 from solokeys/windows_hello_error_codes
Windows hello response codes
2019-07-29 14:09:48 -04:00
690d7c716a move CTAPHID_STATUS_PROCESSING to after UP 2019-07-29 12:39:59 -04:00
78e3b291c2 make sure device status is set in all user presence tests 2019-07-28 22:10:56 -04:00
b47854c335 use error code PIN_AUTH_INVALID 2019-07-28 21:41:11 -04:00
2af747ddaa Merge pull request #229 from solokeys/fix-hmac-secret
Fix hmac secret
2019-07-27 12:49:30 -04:00
9ead11de8d Merge pull request #224 from solokeys/fault_tolerance
limit length of wLength
2019-07-27 12:47:28 -04:00
f17faca689 use correct size for auth_data for signature 2019-07-26 23:53:20 -04:00
ca66b6e43b verify signature for hmac-secret 2019-07-26 23:51:39 -04:00
1cd1b3c295 check attestation signature on all MC requests 2019-07-26 23:50:23 -04:00
df2cff2350 patch hmac final to use correct key 2019-07-26 23:49:55 -04:00
f5d50e001d test assertions work post reboot 2019-07-26 19:00:07 -04:00
235785b225 Bump stable version to 2.4.0 2019-07-17 23:42:56 +02:00
303c42901a limit length of wLength 2019-07-15 11:32:02 -04:00
df2f950e69 Merge pull request #217 from merlokk/extapdu
Extended length apdu, iso14443 chaining and select
2019-07-08 22:03:02 -04:00
10bf4242e1 fail with more import related info 2019-07-08 21:54:48 -04:00
9e95b0075c default no serial printing 2019-07-08 21:54:36 -04:00
ddbe31776c Merge pull request #220 from merlokk/obt_src
added text how to obtain source code
2019-07-08 21:22:48 -04:00
645ca6a5a0 add 3-space list 2019-07-08 18:12:28 +03:00
15fc39faed added text how to obtain source code 2019-07-08 17:58:57 +03:00
a1eedc0048 small fix 2019-07-06 13:09:19 +03:00
89e00482e4 some improvements 2019-07-06 12:52:23 +03:00
533ce39237 fix nfc_cc length 2019-07-06 00:15:21 +03:00
63ee003535 Merge pull request #202 from winksaville/patch-1
Update building.md
2019-07-05 10:29:40 -04:00
fa9408d5d6 fix u2f tests 2019-07-05 12:39:32 +03:00
ed9689435d APDU_FIDO_U2F_VERSION 2019-07-05 12:33:23 +03:00
24a006068d fix extended apdu decode 2019-07-05 12:25:46 +03:00
315b6564ab u2f works with extended apdu and now user presence not needs if request come from nfc and power from usb 2019-07-04 23:12:31 +03:00
4d9285085f fix tests 2019-07-04 20:42:24 +03:00
2272e69e15 fix tests 2019-07-04 20:14:24 +03:00
151e1d0e9b fix some errors in tests 2019-07-04 20:09:47 +03:00
d1df8b8b77 u2f authenticate fix 2019-07-04 19:54:00 +03:00
cb76c34ed2 fix addressing 2019-07-04 19:45:09 +03:00
f2ebaf6abe invalid cla and r-block works 2019-07-04 19:14:26 +03:00
4845d2c172 fix 14443 apdu decode and select 2019-07-04 17:52:00 +03:00
75b1d9cd01 offset calc refactoring 2019-07-04 17:38:34 +03:00
26bc8a2889 apdu decoding works 2019-07-04 17:27:03 +03:00
88a8eba424 gitignore 2019-07-04 16:32:11 +03:00
d2c85881e6 applet selection and apdu check 2019-07-04 16:29:30 +03:00
236498ee03 add make 2019-07-04 16:27:57 +03:00
a51c9192b1 add apdu_decode 2019-07-04 16:27:33 +03:00
4dc6bcf771 apdu decode sketch 2019-07-03 23:01:37 +03:00
cce81b23d9 Merge branch 'master' of https://github.com/merlokk/solo into extapdu 2019-07-03 22:59:41 +03:00
8c2e2386a9 fix NFC applet selection does not work correctly #213 2019-07-03 20:35:50 +03:00
c783a1442a Merge pull request #215 from merlokk/nfc-testing
Nfc testing
2019-07-03 11:37:19 -04:00
b61e5db736 style 2019-07-03 17:57:27 +03:00
b41cd5d5b8 add nfc test force flag 2019-07-03 17:54:53 +03:00
b42e990f67 format fix 2019-07-03 01:39:38 +03:00
ff53bb1e32 fix style 2019-07-03 01:16:55 +03:00
2d72e02051 remove unused lib 2019-07-03 01:03:34 +03:00
91c77da179 cbor.loads changed to cbor.decode_from 2019-07-03 00:43:51 +03:00
795cf5c4a1 selecting NFC key works 2019-07-02 19:55:04 +03:00
d1722b85af add library not found error 2019-07-02 19:45:46 +03:00
2c500fe25a check pyscard module first 2019-06-28 12:32:52 +03:00
751b2fd69c add nfc device search 2019-06-28 12:16:59 +03:00
c2216929a9 Create SECURITY.md 2019-06-14 00:19:14 +02:00
3f225f362f Update building.md
Adding `solo` as a prerequesite, it's required by `make build-hacker` to merge the hex files.
2019-05-29 15:11:18 -07:00
dd4ff920ad Merge pull request #200 from solokeys/persistedkey
use persisted key info
2019-05-28 18:36:50 -04:00
bddd60c080 use persisted key info 2019-05-27 13:54:29 -04:00
5f878ff022 Merge pull request #196 from solokeys/fido2-conformance
Fido2 conformance
2019-05-19 12:24:36 -04:00
14f91a6e15 add screenshots of tests passing 2019-05-19 12:24:15 -04:00
cd29a0e0fe python black 2019-05-18 21:11:22 -04:00
46b7f9a778 add UP as UV method for when PIN is not set 2019-05-18 14:35:30 -04:00
31328fe7e7 dont fail when public key type is too large 2019-05-18 14:34:54 -04:00
035b1a8632 Merge pull request #195 from solokeys/bump_2.3.0
Update STABLE_VERSION
2019-05-13 23:11:33 +02:00
b1563dbe94 Update STABLE_VERSION 2019-05-13 16:43:14 -04:00
2a9e3ac576 Merge pull request #194 from solokeys/sanitize
fix potential memory leaks
2019-05-13 16:10:42 -04:00
e1474e8e8e fix potential memory leaks 2019-05-13 15:32:04 -04:00
1564df5305 Merge pull request #192 from solokeys/cap
Capacitive touch sensing
2019-05-13 14:30:37 -04:00
1f3db3fe51 Fix image in README 2019-05-12 09:41:12 -07:00
36876e1528 fix build 2019-05-10 15:57:57 -04:00
0f50ae7d63 change u2f to return early if button not immediately pressed 2019-05-10 15:56:52 -04:00
4854192c63 decrease sensitivity slightly 2019-05-09 18:37:17 -04:00
e105afd647 fix build 2019-05-09 17:51:41 -04:00
9fb02d4da3 add UP wait HID messages to U2F for windows 2019-05-09 17:46:01 -04:00
e402d36bf1 fix user presence skipping for nfc 2019-05-09 17:26:28 -04:00
54792b345c status fix 2019-05-09 16:07:05 -04:00
84740f3d6a changes to make firmware interop on all hw models 2019-05-09 16:01:07 -04:00
4ac61f7f18 slight cleanup 2019-05-09 14:53:22 -04:00
30cfa46186 fix gpio pin reading 2019-05-09 14:17:50 -04:00
aca28fde61 add to bootloader 2019-05-09 02:44:17 -04:00
60e3d01e0d refactor 2019-05-09 02:44:04 -04:00
aff8d10432 connect to application 2019-05-09 02:26:32 -04:00
898d45f871 bugfix 2019-05-09 01:34:54 -04:00
2b2835b823 initial cap sensing boilerplate 2019-05-08 22:26:57 -04:00
f9202b2b6a Merge pull request #186 from solokeys/bump2.2.2
bump
2019-04-24 20:39:00 -04:00
1b74f6a93b bump 2019-04-24 20:38:00 -04:00
0dfda6fce2 Merge pull request #185 from solokeys/wallet-api
Fix FIDO2 get assertion issues with "custom" requests
2019-04-24 20:25:19 -04:00
09b73d694f fix build 2019-04-24 19:33:26 -04:00
9ab5e761c3 Update tinycbor 2019-04-24 19:04:33 -04:00
b3604f49ba use ctap2 instead of client 2019-04-24 19:00:32 -04:00
6ae1cd3865 remove not-useful logs 2019-04-24 18:36:36 -04:00
f9d3b9561d test that getassertions are correct for solo ext 2019-04-24 18:27:11 -04:00
ec98af115f restore button in ctap_make_auth_data 2019-04-24 16:54:26 -04:00
fecf258116 disable wallet by default 2019-04-24 16:44:25 -04:00
437f691d12 Update solo.c 2019-04-24 16:41:22 -04:00
55aadfd78e add delay implementation 2019-04-24 11:45:41 -04:00
813eb97d2f reuse memory for allow_list of creds 2019-04-24 11:45:30 -04:00
32afdccfb3 Rebump 2019-04-24 17:41:08 +02:00
41ae0e4a2c Update application.mk 2019-04-24 00:16:19 -04:00
b0baace2e7 move custom credid to different location 2019-04-24 00:15:32 -04:00
6ff4200f5d unbump 2019-04-23 21:58:26 -04:00
1fab0b8f1f add wallet api in as compile option 2019-04-23 21:57:50 -04:00
ce96fffddd add info to authData for ext reqs 2019-04-23 21:57:27 -04:00
4fb25e165a remove old web assets 2019-04-23 21:15:11 -04:00
8fc0da7934 move args to device_init 2019-04-23 21:12:40 -04:00
494e856198 Merge pull request #131 from rgerganov/hidg
Add support for hidg devices on Linux
2019-04-23 20:22:33 -04:00
472b094acb bump 2019-04-23 20:21:19 -04:00
e0ce23034f Merge pull request #180 from solokeys/test_pin_fixes
Test pin fixes
2019-04-23 19:58:44 -04:00
56d6624e4e Test correct alg parameter #179 2019-04-22 16:26:26 -04:00
3094c87b0a Test empty pinAuth in MC and GA #179 2019-04-22 16:25:08 -04:00
6a288243c1 Add Makefile for installing a HID gadget
The Makefile has targets for building, installing and uninstalling a HID
gadget which acts as FIDO2 authenticator.

Tested on Ubuntu 18.04 but should work on other distros as well. The
only assumption being made is that kernel source is available at
/usr/src/linux-source-$(KERNEL_VERSION).tar.bz2. A possible improvement
would be to have a configure script which finds the correct kernel
source archive.
2019-04-16 10:54:49 +03:00
955d4f76ef Add support for hidg devices on Linux
There is a HID gadget driver on Linux which provides emulation of USB
HID devices. This could be very useful for testing the Solo firmware
without actual hardware, using only a Linux box.

This patch adds a command line argument which specifies whether the
existing UDP backing should be used or the new one which reads and
writes to /dev/hidg0.

Testing done:
 1. Created HID device with configfs
 2. Started "./main -b hidg" as root
 3. Successfully executed Webauthn registration and authentication on
 the same Linux machine

Closes: #122
2019-04-16 10:54:46 +03:00
81 changed files with 4853 additions and 15605 deletions

197
.all-contributorsrc Normal file
View File

@ -0,0 +1,197 @@
{
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"contributors": [
{
"login": "szszszsz",
"name": "Szczepan Zalega",
"avatar_url": "https://avatars0.githubusercontent.com/u/17005426?v=4",
"profile": "https://github.com/szszszsz",
"contributions": [
"code",
"doc",
"ideas"
]
},
{
"login": "Wesseldr",
"name": "Wessel dR",
"avatar_url": "https://avatars1.githubusercontent.com/u/4012809?v=4",
"profile": "https://github.com/Wesseldr",
"contributions": [
"doc"
]
},
{
"login": "agl",
"name": "Adam Langley",
"avatar_url": "https://avatars3.githubusercontent.com/u/21203?v=4",
"profile": "https://www.imperialviolet.org",
"contributions": [
"bug",
"code"
]
},
{
"login": "merlokk",
"name": "Oleg Moiseenko",
"avatar_url": "https://avatars2.githubusercontent.com/u/807634?v=4",
"profile": "http://www.lotteam.com",
"contributions": [
"code"
]
},
{
"login": "aseigler",
"name": "Alex Seigler",
"avatar_url": "https://avatars1.githubusercontent.com/u/6605560?v=4",
"profile": "https://github.com/aseigler",
"contributions": [
"bug"
]
},
{
"login": "dschuermann",
"name": "Dominik Schürmann",
"avatar_url": "https://avatars3.githubusercontent.com/u/321888?v=4",
"profile": "https://www.cotech.de/services/",
"contributions": [
"bug"
]
},
{
"login": "ehershey",
"name": "Ernie Hershey",
"avatar_url": "https://avatars0.githubusercontent.com/u/286008?v=4",
"profile": "https://github.com/ehershey",
"contributions": [
"doc"
]
},
{
"login": "YakBizzarro",
"name": "Andrea Corna",
"avatar_url": "https://avatars1.githubusercontent.com/u/767740?v=4",
"profile": "https://github.com/YakBizzarro",
"contributions": [
"infra"
]
},
{
"login": "pjz",
"name": "Paul Jimenez",
"avatar_url": "https://avatars3.githubusercontent.com/u/11100?v=4",
"profile": "https://place.org/~pj/",
"contributions": [
"infra",
"code"
]
},
{
"login": "yparitcher",
"name": "yparitcher",
"avatar_url": "https://avatars0.githubusercontent.com/u/38916402?v=4",
"profile": "https://github.com/yparitcher",
"contributions": [
"ideas",
"maintenance"
]
},
{
"login": "StoyanDimitrov",
"name": "StoyanDimitrov",
"avatar_url": "https://avatars1.githubusercontent.com/u/10962709?v=4",
"profile": "https://github.com/StoyanDimitrov",
"contributions": [
"doc"
]
},
{
"login": "alphathegeek",
"name": "alphathegeek",
"avatar_url": "https://avatars2.githubusercontent.com/u/51253712?v=4",
"profile": "https://github.com/alphathegeek",
"contributions": [
"ideas"
]
},
{
"login": "rgerganov",
"name": "Radoslav Gerganov",
"avatar_url": "https://avatars2.githubusercontent.com/u/271616?v=4",
"profile": "https://xakcop.com",
"contributions": [
"ideas",
"code"
]
},
{
"login": "manuel-domke",
"name": "Manuel Domke",
"avatar_url": "https://avatars3.githubusercontent.com/u/10274356?v=4",
"profile": "http://13-37.org",
"contributions": [
"ideas",
"code",
"business"
]
},
{
"login": "esden",
"name": "Piotr Esden-Tempski",
"avatar_url": "https://avatars3.githubusercontent.com/u/17334?v=4",
"profile": "http://1bitsquared.com",
"contributions": [
"business"
]
},
{
"login": "m3hm00d",
"name": "f.m3hm00d",
"avatar_url": "https://avatars1.githubusercontent.com/u/42179593?v=4",
"profile": "https://github.com/m3hm00d",
"contributions": [
"doc"
]
},
{
"login": "hughsie",
"name": "Richard Hughes",
"avatar_url": "https://avatars0.githubusercontent.com/u/151380?v=4",
"profile": "http://blogs.gnome.org/hughsie/",
"contributions": [
"ideas",
"code",
"infra",
"tool"
]
},
{
"login": "kimusan",
"name": "Kim Schulz",
"avatar_url": "https://avatars1.githubusercontent.com/u/1150049?v=4",
"profile": "http://www.schulz.dk",
"contributions": [
"business",
"ideas"
]
},
{
"login": "oplik0",
"name": "Jakub",
"avatar_url": "https://avatars2.githubusercontent.com/u/25460763?v=4",
"profile": "https://github.com/oplik0",
"contributions": [
"bug"
]
}
],
"contributorsPerLine": 7,
"projectName": "solo",
"projectOwner": "solokeys",
"repoType": "github",
"repoHost": "https://github.com"
}

2
.gitignore vendored
View File

@ -83,3 +83,5 @@ targets/*/docs/
main
builds/*
tools/testing/.idea/*
tools/testing/tests/__pycache__/*

View File

@ -1,40 +0,0 @@
# 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.
## [2.1.0] - 2019-03-31
WARNING: This update may break previous registrations! This is because we fixed the U2F counter for good (rather than arbitrarily set the upper byte high for backwards-compatibility reasons, which ends up causing other issues).
- Adds hmac-secret extension support. This extension is used for generating 32 or 64 byte symmetric keys using parameters from the platform and secrets on the authenticator. It's used by Windows Hello - - for offline authentication.
- Fix bug in FIDO auth, where setting the pin requires all previous registrations to use pin. Only UV bit needs to be cleared.
- Slightly change serial emulation USB descriptor to make it less abused by Linux Modem Manager.
## [2.2.0] - 2019-04-17
- Fixes the ordering of keys encoded in CBOR maps to be canonical ordering. They previously were not ordered in any particular way and caused issues for Chrome. #170
- Fixes CTAP2 implementation to accept credential IDs created by the CTAP1 implementation. So registering with U2F and later authenticating with FIDO2 should work.

View File

@ -32,7 +32,7 @@ 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)
CFLAGS = -O2 -fdata-sections -ffunction-sections $(VERSION_FLAGS) -g
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
@ -53,7 +53,7 @@ tinycbor/Makefile crypto/tiny-AES-c/aes.c:
cbor: $(LIBCBOR)
$(LIBCBOR):
cd tinycbor/ && $(MAKE) clean && $(MAKE) -j8
cd tinycbor/ && $(MAKE) clean && $(MAKE) LDFLAGS='' -j8
version:
@git describe

View File

@ -1,23 +1,18 @@
[![License](https://img.shields.io/github/license/solokeys/solo.svg)](https://github.com/solokeys/solo/blob/master/LICENSE)
[![Build Status](https://travis-ci.com/solokeys/solo.svg?branch=master)](https://travis-ci.com/solokeys/solo)
[![Discourse Users](https://img.shields.io/discourse/https/discourse.solokeys.com/users.svg)](https://discourse.solokeys.com)
**NEW!** We launched a new tiny security key called Somu, it's live on Crowd Supply and you can [pre-order it now](https://solokeys.com/somu)!
[<img src="https://miro.medium.com/max/1400/1*PnzCPLqq_5nt1gjgSEY2LQ.png" width="600">](https://solokeys.com/somu)
Somu is the micro version of Solo. We were inspired to make a secure Tomu, so we took its tiny form factor, we added the secure microcontroller and firmware of Solo, et voilà! Here we have Somu.
[![latest release](https://img.shields.io/github/release/solokeys/solo.svg)](https://update.solokeys.com/)
[![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
[![Build Status](https://travis-ci.com/solokeys/solo.svg?style=flat-square&branch=master)](https://travis-ci.com/solokeys/solo)
Solo is an open source security key, and you can get one at [solokeys.com](https://solokeys.com).
Solo supports FIDO2 and U2F standards for strong two-factor authentication and password-less login, and it will protect you against phishing and other online attacks. With colored cases and multilingual guides we want to make secure login more personable and accessible to everyone around the globe.
[<img src="https://static.solokeys.com/images/photos/hero-on-white-cropped.png" width="600">](https://solokeys.com)
<img src="https://solokeys.com/images/photos/hero-on-white-cropped.png" width="600">
Solo supports FIDO2 and U2F standards for strong two-factor authentication and password-less login, and it will protect you against phishing and other online attacks. With colored cases and multilingual guides we want to make secure login more personable and accessible to everyone around the globe.
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.
@ -41,7 +36,7 @@ Solo for Hacker is a special version of Solo that let you customize its firmware
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/). We only support Python3.
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 support Python3.
```bash
git clone --recurse-submodules https://github.com/solokeys/solo
@ -94,10 +89,7 @@ Run the Solo application:
./main
```
In another shell, you can run client software, for example our tests:
```bash
python tools/ctap_test.py sim fido2
```
In another shell, you can run our [test suite](https://github.com/solokeys/fido2-tests).
You can find more details in our [documentation](https://docs.solokeys.io/solo/), including how to build on the the NUCLEO-L432KC development board.
@ -107,14 +99,48 @@ You can find more details in our [documentation](https://docs.solokeys.io/solo/)
Check out our [official documentation](https://docs.solokeys.io/solo/).
# Contributors
# Contributors
Solo is an upgrade to [U2F Zero](https://github.com/conorpp/u2f-zero). It was born from Conor's passion for making secure hardware, and from our shared belief that security should be open to be trustworthy, in hardware like in software.
Contributors are welcome. The ultimate goal is to have a FIDO2 security key supporting USB, NFC, and BLE interfaces, that can run on a variety of MCUs.
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
The ultimate goal is to have a FIDO2 security key supporting USB, NFC, and BLE interfaces, that can run on a variety of MCUs.
Look at the issues to see what is currently being worked on. Feel free to add issues as well.
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore -->
<table>
<tr>
<td align="center"><a href="https://github.com/szszszsz"><img src="https://avatars0.githubusercontent.com/u/17005426?v=4" width="100px;" alt="Szczepan Zalega"/><br /><sub><b>Szczepan Zalega</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=szszszsz" title="Code">💻</a> <a href="https://github.com/solokeys/solo/commits?author=szszszsz" title="Documentation">📖</a> <a href="#ideas-szszszsz" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/Wesseldr"><img src="https://avatars1.githubusercontent.com/u/4012809?v=4" width="100px;" alt="Wessel dR"/><br /><sub><b>Wessel dR</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=Wesseldr" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.imperialviolet.org"><img src="https://avatars3.githubusercontent.com/u/21203?v=4" width="100px;" alt="Adam Langley"/><br /><sub><b>Adam Langley</b></sub></a><br /><a href="https://github.com/solokeys/solo/issues?q=author%3Aagl" title="Bug reports">🐛</a> <a href="https://github.com/solokeys/solo/commits?author=agl" title="Code">💻</a></td>
<td align="center"><a href="http://www.lotteam.com"><img src="https://avatars2.githubusercontent.com/u/807634?v=4" width="100px;" alt="Oleg Moiseenko"/><br /><sub><b>Oleg Moiseenko</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=merlokk" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/aseigler"><img src="https://avatars1.githubusercontent.com/u/6605560?v=4" width="100px;" alt="Alex Seigler"/><br /><sub><b>Alex Seigler</b></sub></a><br /><a href="https://github.com/solokeys/solo/issues?q=author%3Aaseigler" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://www.cotech.de/services/"><img src="https://avatars3.githubusercontent.com/u/321888?v=4" width="100px;" alt="Dominik Schürmann"/><br /><sub><b>Dominik Schürmann</b></sub></a><br /><a href="https://github.com/solokeys/solo/issues?q=author%3Adschuermann" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/ehershey"><img src="https://avatars0.githubusercontent.com/u/286008?v=4" width="100px;" alt="Ernie Hershey"/><br /><sub><b>Ernie Hershey</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=ehershey" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/YakBizzarro"><img src="https://avatars1.githubusercontent.com/u/767740?v=4" width="100px;" alt="Andrea Corna"/><br /><sub><b>Andrea Corna</b></sub></a><br /><a href="#infra-YakBizzarro" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://place.org/~pj/"><img src="https://avatars3.githubusercontent.com/u/11100?v=4" width="100px;" alt="Paul Jimenez"/><br /><sub><b>Paul Jimenez</b></sub></a><br /><a href="#infra-pjz" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/solokeys/solo/commits?author=pjz" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/yparitcher"><img src="https://avatars0.githubusercontent.com/u/38916402?v=4" width="100px;" alt="yparitcher"/><br /><sub><b>yparitcher</b></sub></a><br /><a href="#ideas-yparitcher" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-yparitcher" title="Maintenance">🚧</a></td>
<td align="center"><a href="https://github.com/StoyanDimitrov"><img src="https://avatars1.githubusercontent.com/u/10962709?v=4" width="100px;" alt="StoyanDimitrov"/><br /><sub><b>StoyanDimitrov</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=StoyanDimitrov" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/alphathegeek"><img src="https://avatars2.githubusercontent.com/u/51253712?v=4" width="100px;" alt="alphathegeek"/><br /><sub><b>alphathegeek</b></sub></a><br /><a href="#ideas-alphathegeek" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://xakcop.com"><img src="https://avatars2.githubusercontent.com/u/271616?v=4" width="100px;" alt="Radoslav Gerganov"/><br /><sub><b>Radoslav Gerganov</b></sub></a><br /><a href="#ideas-rgerganov" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/solokeys/solo/commits?author=rgerganov" title="Code">💻</a></td>
<td align="center"><a href="http://13-37.org"><img src="https://avatars3.githubusercontent.com/u/10274356?v=4" width="100px;" alt="Manuel Domke"/><br /><sub><b>Manuel Domke</b></sub></a><br /><a href="#ideas-manuel-domke" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/solokeys/solo/commits?author=manuel-domke" title="Code">💻</a> <a href="#business-manuel-domke" title="Business development">💼</a></td>
</tr>
<tr>
<td align="center"><a href="http://1bitsquared.com"><img src="https://avatars3.githubusercontent.com/u/17334?v=4" width="100px;" alt="Piotr Esden-Tempski"/><br /><sub><b>Piotr Esden-Tempski</b></sub></a><br /><a href="#business-esden" title="Business development">💼</a></td>
<td align="center"><a href="https://github.com/m3hm00d"><img src="https://avatars1.githubusercontent.com/u/42179593?v=4" width="100px;" alt="f.m3hm00d"/><br /><sub><b>f.m3hm00d</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=m3hm00d" title="Documentation">📖</a></td>
<td align="center"><a href="http://blogs.gnome.org/hughsie/"><img src="https://avatars0.githubusercontent.com/u/151380?v=4" width="100px;" alt="Richard Hughes"/><br /><sub><b>Richard Hughes</b></sub></a><br /><a href="#ideas-hughsie" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/solokeys/solo/commits?author=hughsie" title="Code">💻</a> <a href="#infra-hughsie" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#tool-hughsie" title="Tools">🔧</a></td>
<td align="center"><a href="http://www.schulz.dk"><img src="https://avatars1.githubusercontent.com/u/1150049?v=4" width="100px;" alt="Kim Schulz"/><br /><sub><b>Kim Schulz</b></sub></a><br /><a href="#business-kimusan" title="Business development">💼</a> <a href="#ideas-kimusan" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/oplik0"><img src="https://avatars2.githubusercontent.com/u/25460763?v=4" width="100px;" alt="Jakub"/><br /><sub><b>Jakub</b></sub></a><br /><a href="https://github.com/solokeys/solo/issues?q=author%3Aoplik0" title="Bug reports">🐛</a></td>
</tr>
</table>
<!-- ALL-CONTRIBUTORS-LIST:END -->
# License
@ -123,6 +149,8 @@ Solo is fully open source.
All software, unless otherwise noted, is dual licensed under Apache 2.0 and MIT.
You may use Solo software under the terms of either the Apache 2.0 license or MIT license.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
All hardware, unless otherwise noted, is dual licensed under CERN and CC-BY-SA.
You may use Solo hardware under the terms of either the CERN 2.1 license or CC-BY-SA 4.0 license.
@ -135,3 +163,20 @@ You may use Solo documentation under the terms of the CC-BY-SA 4.0 license
# Where To Buy Solo
You can buy Solo, Solo Tap, and Solo for Hackers at [solokeys.com](https://solokeys.com).
<br/>
<hr/>
<br/>
[![License](https://img.shields.io/github/license/solokeys/solo.svg)](https://github.com/solokeys/solo/blob/master/LICENSE)
[![All Contributors](https://img.shields.io/badge/all_contributors-19-orange.svg?style=flat-square)](#contributors)
[![Build Status](https://travis-ci.com/solokeys/solo.svg?branch=master)](https://travis-ci.com/solokeys/solo)
[![Discourse Users](https://img.shields.io/discourse/https/discourse.solokeys.com/users.svg)](https://discourse.solokeys.com)
[![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)

32
SECURITY.md Normal file
View File

@ -0,0 +1,32 @@
# Security Policy
## Supported Versions
We fix security issues as soon as they are found, and release firmware updates.
Each such release is accompanied by release notes, see <https://github.com/solokeys/solo/releases>.
The latest version can be determined using the file <https://github.com/solokeys/solo/blob/master/STABLE_VERSION>.
To update your key:
- either visit <https://update.solokeys.com>, or
- use our commandline tool <https://github.com/solokeys/solo-python>:
```
solo key update [--secure|--hacker]
```
## Reporting a Vulnerability
To report vulnerabilities you have found:
- preferably contact [@conor1](https://keybase.io/conor1), [@0x0ece](https://keybase.io/0x0ece) or [@nickray](https://keybase.io/nickray) via Keybase, or
- send us e-mail using OpenPGP to [security@solokeys.com](mailto:security@solokeys.com).
<https://keys.openpgp.org/vks/v1/by-fingerprint/85AFA2769F4381E5712C36A04DDFC46FEF1F7F3F>
We do not currently run a paid bug bounty program, but are happy to provide you with a bunch of Solo keys in recognition of your findings.
## Mailing List
Join our release notification mailing list to be informed about each release:
https://sendy.solokeys.com/subscription?f=9MLIqMDmox1Ucz89C892Kq09IqYMM7OB8UrBrkvtTkDI763QF3L5PMYlRhlVNo2AI892mO

View File

@ -1 +1 @@
2.2.0
2.5.1

View File

@ -0,0 +1,51 @@
# Booting into bootloader mode
You can put Solo into bootloader mode by holding down the button, and plugging in Solo. After 2 seconds, bootloader mode will activate.
You'll see a yellowish flashing light and you can let go of the button.
Now Solo is ready to [accept firmware updates](/solo/signed-updates). If the Solo is a secured model, it can only accept signed updates, typically in the `firmware-*.json` format.
If Solo is running a hacker build, it can be put into bootloader mode on command. This makes it easier for development.
```bash
solo program aux enter-bootloader
```
# The boot stages of Solo
Solo has 3 boot stages.
## DFU
The first stage is the DFU (Device Firmware Update) which is in a ROM on Solo. It is baked into the chip and is not implemented by us.
This is what allows the entire firmware of Solo to be programmed. **It's not recommended to develop for Solo using the DFU because
if you program broken firmware, you could brick your device**.
On hacker devices, you can boot into the DFU by holding down the button for 5 seconds, when Solo is already in bootloader mode.
You can also run this command when Solo is in bootloader mode to put it in DFU mode.
```bash
solo program aux enter-dfu
```
Note it will stay in DFU mode until to tell it to boot again. You can boot it again by running the following.
```bash
solo program aux leave-dfu
```
*Warning*: If you change the firmware to something broken, and you tell the DFU to boot it, you could brick your device.
## Solo Bootloader
The next boot stage is the "Solo bootloader". So when we say to put your Solo into bootloader mode, it is this stage.
This bootloader is written by us and allows signed firmware updates to be written. On Solo Hackers, there is no signature checking
and will allow any firmware updates.
It is safe to develop for Solo using our Solo bootloader. If broken firmware is uploaded to the device, then the Solo
bootloader can always be booted again by holding down the button when plugging in.
## Solo application
This is what contains all the important functionality of Solo. FIDO2, U2F, etc. This is what Solo will boot to by default.

View File

@ -1,22 +1,34 @@
# Building solo
To build, develop and debug the firmware for the STM32L432. This will work
for Solo Hacker, the Nucleo development board, or your own homemade Solo.
There exists a development board [NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) you can use; The board does contain a debugger, so all you need is a USB cable (and some [udev](/udev) [rules](https://rust-embedded.github.io/book/intro/install/linux.html#udev-rules)).
# Prerequisites
## Prerequisites
Install the [latest ARM compiler toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) for your system. We recommend getting the latest compilers from ARM.
You can also install the ARM toolchain using a package manager like `apt-get` or `pacman`,
but be warned they might be out of date. Typically it will be called `gcc-arm-none-eabi binutils-arm-none-eabi`.
To program your build, you'll need one of the following programs.
Install `solo-python` usually with `pip3 install solo-python`. The `solo` python application may also be used for [programming](#programming).
- [openocd](http://openocd.org)
- [stlink](https://github.com/texane/stlink)
- [STM32CubeProg](https://www.st.com/en/development-tools/stm32cubeprog.html)
## Obtain source code and solo tool
# Compilation
Source code can be downloaded from:
- [github releases list](https://github.com/solokeys/solo/releases)
- [github repository](https://github.com/solokeys/solo)
**solo** tool can be downloaded from:
- from python programs [repository](https://pypi.org/project/solo-python/) `pip install solo-python`
- from installing prerequisites `pip3 install -r tools/requirements.txt`
- github repository: [repository](https://github.com/solokeys/solo-python)
- installation python enviroment with command `make venv` from root directory of source code
## Compilation
Enter the `stm32l4xx` target directory.
@ -36,7 +48,7 @@ enabled, like being able to jump to the bootloader on command. It then merges b
and solo builds into the same binary. I.e. it combines `bootloader.hex` and `solo.hex`
into `all.hex`.
If you're just planning to do development, please don't try to reprogram the bootloader,
If you're just planning to do development, **please don't try to reprogram the bootloader**,
as this can be risky if done often. Just use `solo.hex`.
### Building with debug messages
@ -68,6 +80,8 @@ solo monitor <serial-port>
### Building a Solo release
To build Solo
If you want to build a release of Solo, we recommend trying a Hacker build first
just to make sure that it's working. Otherwise it may not be as easy or possible to
fix any mistakes.
@ -78,106 +92,13 @@ If you're ready to program a full release, run this recipe to build.
make build-release-locked
```
Programming `all.hex` will cause the device to permanently lock itself.
This outputs bootloader.hex, solo.hex, and the combined all.hex.
Programming `all.hex` will cause the device to permanently lock itself. This means debuggers cannot be used and signature checking
will be enforced on all future updates.
# Programming
Note if you program a secured `solo.hex` file onto a Solo Hacker, it will lock the flash, but the bootloader
will still accept unsigned firmware updates. So you can switch it back to being a hacker, but you will
not be able to replace the unlocked bootloader anymore, since the permanently locked flash also disables the DFU.
[Read more on Solo's boot stages](/solo/bootloader-mode).
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 `solo` tool to manage programming. It is cross platform. First you must
install the prerequisites:
```
pip3 install -r tools/requirements.txt
```
If you're on Windows, you must also install [libusb](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/).
## Pre-programmed Solo Hacker
If your Solo device is already programmed (it flashes green when powered), we recommend
programming it using the Solo bootloader.
```
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
see errors.
If something bad happens, you can always boot the Solo bootloader by doing the following.
1. Unplug device.
2. Hold down button.
3. Plug in device while holding down button.
4. Wait about 2 seconds for flashing yellow light. Release button.
If you hold the button for an additional 5 seconds, it will boot to the ST DFU (device firmware update).
Don't use the ST DFU unless you know what you're doing.
## ST USB DFU
If your Solo has never been programmed, it will boot the ST USB DFU. The LED is turned
off and it enumerates as "STM BOOTLOADER".
You can program it by running the following.
```
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.
If all goes well, you should see a slow-flashing green light.
## Solo Hacker vs Solo
A Solo hacker device doesn't need to be in bootloader mode to be programmed, it will automatically switch.
Solo (locked) needs the button to be held down when plugged in to boot to the bootloader.
A locked Solo will only accept signed updates.
## Signed updates
If this is not a device with a hacker build, you can only program signed updates.
```
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.
```
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
to the token.
# Permanently locking the device
If you plan to be using your Solo for real, you should lock it permanently. This prevents
someone from connecting a debugger to your token and stealing credentials.
To do this, build the locked release firmware.
```
make build-release-locked
```
Now when you program `all.hex`, the device will lock itself when it first boots. You can only update it
with signed updates.
If you'd like to also permanently disable signed updates, plug in your programmed Solo and run the following:
```
# WARNING: No more signed updates.
solo program disable-bootloader
```

141
docs/solo/customization.md Normal file
View File

@ -0,0 +1,141 @@
# Customization
If you are interested in customizing parts of your Solo, and you have a Solo Hacker, this page is for you.
## Custom Attestation key
The attestation key is used in the FIDO2 *makeCredential* or U2F *register* requests. It signs
newly generated credentials. The certificate associated with the attestation key is output with newly created credentials.
Platforms or services can use the attestation feature to enforce specific authenticators to be used.
This is typically a use case for organizations and isn't seen in the wild for consumer use cases.
Attestation keys are typically the same for at least 100K units of a particular authenticator model.
This is so they don't contribute a significant fingerprint that platforms could use to identify the user.
If you don't want to use the default attestation key that Solo builds with, you can create your own
and program it.
### Creating your attestation key pair
Since we are generating keys, it's important to use a good entropy source.
You can use the [True RNG on your Solo](/solo/solo-extras) to generate some good random numbers.
```
# Run for 1 second, then hit control-c
solo key rng raw > seed.bin
```
First we will create a self signed key pair that acts as the root of trust. This
won't go on the authenticator, but will sign the keypair that does.
Please change the root certification information as needed. You may change the ECC curve.
```
curve=prime256v1
country=US
state=Maine
organization=OpenSourceSecurity
unit="Root CA"
CN=example.com
email=example@example.com
# generate EC private key
openssl ecparam -genkey -name "$curve" -out root_key.pem -rand seed.bin
# generate a "signing request"
openssl req -new -key root_key.pem -out root_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=example.com/emailAddress=$email"
# self sign the request
openssl x509 -trustout -req -days 18250 -in root_key.pem.csr -signkey root_key.pem -out root_cert.pem -sha256
# convert to smaller size format DER
openssl x509 -in root_cert.pem -outform der -out root_cert.der
# print out information and verify
openssl x509 -in root_cert.pem -text -noout
```
You need to create a extended certificate for the device certificate to work with FIDO2. You need to create this
file, `v3.ext`, and add these options to it.
```
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
```
Now to generate & sign the attestation key pair that will go on your device, or maybe 100,000 devices :).
Note you must use a prime256v1 curve for this step, and you must leave the unit/OU as "Authenticator Attestation".
```
country=US
state=Maine
organization=OpenSourceSecurity
unit="Authenticator Attestation"
CN=example.com
email=example@example.com
# generate EC private key
openssl ecparam -genkey -name "$curve" -out device_key.pem -rand seed.bin
# generate a "signing request"
openssl req -new -key device_key.pem -out device_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=example.com/emailAddress=$email"
# sign the request
openssl x509 -req -days 18250 -in device_key.pem.csr -extfile v3.ext -CA root_cert.pem -CAkey root_key.pem -set_serial 01 -out device_cert.pem -sha256
# convert to smaller size format DER
openssl x509 -in device_cert.pem -outform der -out device_cert.der
# Verify the device certificate details
openssl x509 -in device_cert.pem -text -noout
```
Let's verify that the attestation key and certificate are valid, and that they can be verified with the root key pair.
```
echo 'challenge $RANDOM' > chal.txt
# check that they are valid key pairs
openssl dgst -sha256 -sign device_key.pem -out sig.txt chal.txt
openssl dgst -sha256 -verify <(openssl x509 -in device_cert.pem -pubkey -noout) -signature sig.txt chal.txt
openssl dgst -sha256 -sign "root_key.pem" -out sig.txt chal.txt
openssl dgst -sha256 -verify <(openssl x509 -in root_cert.pem -pubkey -noout) -signature sig.txt chal.txt
# Check they are a chain
openssl verify -verbose -CAfile "root_cert.pem" "device_cert.pem"
```
If the checks succeed, you are ready to program the device attestation key and certificate.
### Programming an attestation key and certificate
Convert the DER format of the device attestation certificate to "C" bytes using our utility script. You may first need to
first install prerequisite python modules (pip install -r tools/requirements.txt).
```
python tools/gencert/cbytes.py device_cert.der
```
Copy the byte string portion into the [`attestation.c` source file of Solo](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/attestation.c). Overwrite the development or "default" certificate that is already there.
Now [build the Solo firmware](/solo/building), either a secure or hacker build. You will need to produce a bootloader.hex file and a solo.hex file.
Print your attestation key in a hex string format.
```
python tools/print_x_y.py device_key.pem
```
Merge the bootloader.hex, solo.hex, and attestion key into one firmware file.
```
solo mergehex --attestation-key <attestation-key-hex-string> bootloader.hex solo.hex all.hex
```
Now you have a newly create `all.hex` file with a custom attestation key. You can [program this all.hex file
with Solo in DFU mode](/solo/programming#procedure).

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

258
docs/solo/nucleo32-board.md Normal file
View File

@ -0,0 +1,258 @@
# Nucleo32 board preparation
Additional steps are required to run the firmware on the Nucleo32 board.
## USB-A cable
Board does not provide an USB cable / socket for the target MCU communication.
Own provided USB plug has to be connected in the following way:
| PIN / Arduino PIN | MCU leg | USB wire color | Signal |
| ----------------- | ------- | -------------- | ------ |
| D10 / PA11 | 21 | white | D- |
| D2 / PA12 | 22 | green | D+ |
| GND (near D2) | ------- | black | GND |
| **not connected** | ------- | red | 5V |
Each USB plug pin should be connected via the wire in a color defined by the standard. It might be confirmed with a
multimeter for additional safety. USB plug description:
| PIN | USB wire color | Signal |
| --- | -------------- | ------ |
| 4 | black | GND |
| 3 | green | D+ |
| 2 | white | D- |
| 1 | red | 5V |
See this [USB plug] image, and Wikipedia's [USB plug description].
Plug in [USB-A_schematic.pdf] has wrong wire order, registered as [solo-hw#1].
The power is taken from the debugger / board (unless the board is configured in another way).
Make sure 5V is not connected, and is covered from contacting with the board elements.
Based on [USB-A_schematic.pdf].
## Firmware modification
Following patch has to be applied to skip the user presence confirmation, for tests. Might be applied at a later stage.
```text
diff --git a/targets/stm32l432/src/app.h b/targets/stm32l432/src/app.h
index c14a7ed..c89c3b5 100644
--- a/targets/stm32l432/src/app.h
+++ b/targets/stm32l432/src/app.h
@@ -71,6 +71,6 @@ void hw_init(void);
#define SOLO_BUTTON_PIN LL_GPIO_PIN_0
#define SKIP_BUTTON_CHECK_WITH_DELAY 0
-#define SKIP_BUTTON_CHECK_FAST 0
+#define SKIP_BUTTON_CHECK_FAST 1
#endif
```
It is possible to provide a button and connect it to the MCU pins, as instructed in [USB-A_schematic.pdf]&#x3A;
```text
PA0 / pin 6 --> button --> GND
```
In that case the mentioned patch would not be required.
## Development environment setup
Environment: Fedora 29 x64, Linux 4.19.9
See <https://docs.solokeys.io/solo/building/> for the original guide. Here details not included there will be covered.
### Install ARM tools Linux
1. Download current [ARM tools] package: [gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2].
2. Extract the archive.
3. Add full path to the `./bin` directory as first entry to the `$PATH` variable,
as in `~/gcc-arm/gcc-arm-none-eabi-8-2018-q4-major/bin/:$PATH`.
### Install ARM tools OsX using brew package manager
```bash
brew tap ArmMbed/homebrew-formulae
brew install arm-none-eabi-gcc
```
### Install flashing software
ST provides a CLI flashing tool - `STM32_Programmer_CLI`. It can be downloaded directly from the vendor's site:
1\. Go to [download site URL](https://www.st.com/content/st_com/en/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-programmers/stm32cubeprog.html),
go to bottom page and from STM32CubeProg row select Download button.
2\. Unzip contents of the archive.
3\. Run \*Linux setup
4\. In installation directory go to ./bin - there the ./STM32_Programmer_CLI is located
5\. Add symlink to the STM32 CLI binary to .local/bin. Make sure the latter it is in $PATH.
If you're on OsX and installed the STM32CubeProg, you need to add the following to your path:
```bash
# ~/.bash_profile
export PATH="/Applications/STMicroelectronics/STM32Cube/STM32CubeProgrammer/STM32CubeProgrammer.app/Contents/MacOs/bin/":$PATH
```
## Building and flashing
### Building
Please follow <https://docs.solokeys.io/solo/building/>, as the build way changes rapidly.
Currently (8.1.19) to build the firmware, following lines should be executed
```bash
# while in the main project directory
cd targets/stm32l432
make cbor
make build-hacker DEBUG=1
```
Note: `DEBUG=2` stops the device initialization, until a serial client will be attached to its virtual port.
Do not use it, if you do not plan to do so.
### Flashing via the Makefile command
```bash
# while in the main project directory
# create Python virtual environment with required packages, and activate
make venv
. venv/bin/activate
# Run flashing
cd ./targets/stm32l432
make flash
# which runs:
# flash: solo.hex bootloader.hex
# python merge_hex.py solo.hex bootloader.hex all.hex (intelhex library required)
# STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
# STM32_Programmer_CLI -c port=SWD -halt -d all.hex -rst
```
### Manual flashing
In case you already have a firmware to flash (named `all.hex`), please run the following:
```bash
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
STM32_Programmer_CLI -c port=SWD -halt -d all.hex -rst
```
## Testing
### Internal
Project-provided tests.
#### Simulated device
A simulated device is provided to test the HID layer.
##### Build
```bash
make clean
cd tinycbor
make
cd ..
make env2
```
##### Execution
```bash
# run simulated device (will create a network UDP server)
./main
# run test 1
./env2/bin/python tools/ctap_test.py
# run test 2 (or other files in the examples directory)
./env2/bin/python python-fido2/examples/credential.py
```
#### Real device
```bash
# while in the main project directory
# not passing as of 8.1.19, due to test solution issues
make fido2-test
```
### External
#### FIDO2 test sites
1. <https://www.passwordless.dev/overview>
2. <https://webauthn.bin.coffee/>
3. <https://webauthn.org/>
#### U2F test sites
1. <https://u2f.bin.coffee/>
2. <https://demo.yubico.com/u2f>
#### FIDO2 standalone clients
1. <https://github.com/Nitrokey/u2f-ref-code>
2. <https://github.com/Yubico/libfido2>
3. <https://github.com/Yubico/python-fido2>
4. <https://github.com/google/pyu2f>
## USB serial console reading
Device opens an USB-emulated serial port to output its messages. While Nucleo board offers such already,
the Solo device provides its own.
- Provided Python tool
```bash
python3 ../../tools/solotool.py monitor /dev/solokey-serial
```
- External application
```bash
sudo picocom -b 115200 /dev/solokey-serial
```
where `/dev/solokey-serial` is an udev symlink to `/dev/ttyACM1`.
## Other
### Dumping firmware
Size is calculated using bash arithmetic.
```bash
STM32_Programmer_CLI -c port=SWD -halt -u 0x0 $((256*1024)) current.hex
```
### Software reset
```bash
STM32_Programmer_CLI -c port=SWD -rst
```
### Installing required Python packages
Client script requires some Python packages, which could be easily installed locally to the project
via the Makefile command. It is sufficient to run:
```bash
make env3
```
[solo-hw#1]: https://github.com/solokeys/solo-hw/issues/1
[usb plug]: https://upload.wikimedia.org/wikipedia/commons/thumb/6/67/USB.svg/1200px-USB.svg.png
[usb plug description]: https://en.wikipedia.org/wiki/USB#Receptacle_(socket)_identification
[usb-a_schematic.pdf]: https://github.com/solokeys/solo-hw/releases/download/1.2/USB-A_schematic.pdf
[arm tools]: https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads
[gcc-arm-none-eabi-8-2018-q4-major-linux.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

113
docs/solo/programming.md Normal file
View File

@ -0,0 +1,113 @@
# Programming
This page documents how to update or program your Solo.
## Prerequisites
To program Solo, you'll likely only need to use our Solo tool.
```python
pip3 install solo-python
```
## Updating the firmware
If you just want to update the firmware, you can run one of the following commands.
Make sure your key [is in bootloader mode](/solo/bootloader-mode#solo-bootloader) first.
```bash
solo key update <--secure | --hacker>
```
You can manually install the [latest release](https://github.com/solokeys/solo/releases), or use a build that you made.
```bash
# If it's a hacker, it will automatically boot into bootloader mode.
solo program bootloader <firmware.hex | firmware.json>
```
Note you won't be able to use `all.hex` or the `bundle-*.hex` builds, as these include the solo bootloader. You shouldn't
risk changing the Solo bootloader unless you want to make it a secure device, or [make other customizations]().
## Updating a Hacker to a Secure Solo
Updating a hacker to be a secure build overwrites the [Solo bootloader](/solo/bootloader-mode#solo-bootloader).
So it's important to not mess this up or you may brick your device.
You can use a firmware build from the [latest release](https://github.com/solokeys/solo/releases) or use
a build that you made yourself.
You need to use a firmware file that has the combined bootloader and application (or at the very least just the bootloader).
This means using the `bundle-*.hex` file or the `all.hex` from your build. If you overwrite the Solo flash with a missing bootloader,
it will be bricked.
We provide two types of bundled builds. The `bundle-hacker-*.hex` build is the hacker build. If you update with this,
you will update the bootloader and application, but nothing will be secured. The `bundle-secure-non-solokeys.hex`
is a secured build that will lock your device and it will behave just like a Secure Solo. The main difference is that
it uses a "default" attestation key in the device, rather than the SoloKeys attestation key. There is no security
concern with using our default attestation key, aside from a privacy implication that services can distinguish it from Solo Secure.
### Procedure
1. Boot into DFU mode.
# Enter Solo bootloader
solo program aux enter-bootloader
# Enter DFU
solo program aux enter-dfu
The device should be turned off.
2. Program the device
solo program dfu <bundle-secure-non-solokeys.hex | all.hex>
Double check you programmed it with bootloader + application (or just bootloader).
If you messed it up, simply don't do the next step and repeat this step correctly.
3. Boot the device
Once Solo boots a secure build, it will lock the flash permantly from debugger access. Also the bootloader
will only accept signed firmware updates.
solo program aux leave-dfu
If you are having problems with solo tool and DFU mode, you could alternatively try booting into DFU
by holding down the button while Solo is in bootloader mode. Then try another programming tool that works
with ST DFU:
* STM32CubeProg
* openocd
* stlink
Windows users need to install [libusb](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/)
for solo-python to work with Solo's DFU.
## Programming a Solo that hasn't been programmed
A Solo that hasn't been programmed will boot into DFU mode. You can program
it by following a bootloader, or combined bootloader + application.
```
solo program dfu <bundle-*.hex | all.hex>
```
Then boot the device. Make sure it has a bootloader to boot to.
```
solo program aux leave-dfu
```
## Disable signed firmware updates
If you'd like to also permanently disable signed updates, plug in your programmed Solo and run the following:
```bash
# WARNING: No more signed updates.
solo program disable-bootloader
```
You won't be able to update to any new releases.

19
docs/solo/solo-extras.md Normal file
View File

@ -0,0 +1,19 @@
# Solo Extras
## Random number generation
Solo contains a True Random Number Generator (TRNG). A TRNG is a hardware based mechanism
that leverages natural phenomenon to generate random numbers, which is can be better than a traditional
RNG that has state and updates deterministically using cryptographic methods.
You can easily access the TRNG stream on Solo using our python tool [solo-python](https://github.com/solokeys/solo-python).
```
solo key rng raw > random.bin
```
Or you can seed the state of the RNG on your kernel (/dev/random).
```
solo key rng feedkernel
```

View File

@ -1,20 +1,21 @@
# Summary
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.)
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.
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):
For some users, things will work automatically:
```
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", MODE="0660", GROUP="plugdev"
```
- Fedora seems to use a ["universal" udev rule for FIDO devices](https://github.com/amluto/u2f-hidraw-policy)
- Our udev rule made it into [libu2f-host](https://github.com/Yubico/libu2f-host/) v1.1.10
- Arch Linux [has this package](https://www.archlinux.org/packages/community/x86_64/libu2f-host/)
- [Debian sid](https://packages.debian.org/sid/libu2f-udev) and [Ubuntu Eon](https://packages.ubuntu.com/eoan/libu2f-udev) can use the `libu2f-udev` package
- Debian Buster and Ubuntu Disco still distribute v1.1.10, so need the manual rule
- FreeBSD has support in [u2f-devd](https://github.com/solokeys/solo/issues/144#issuecomment-500216020)
Additionally, run the following command after you create this file (it is not necessary to do this again in the future):
There is hope that `udev` itself will adopt the Fedora approach (which is to check for HID usage page `F1D0`, and avoids manually whitelisting each U2F/FIDO2 key): <https://github.com/systemd/systemd/issues/11996>.
```
sudo udevadm control --reload-rules && sudo udevadm trigger
```
Further progress is tracked in: <https://github.com/solokeys/solo/issues/144>.
A simple way to setup both the udev rule and the udevadm reload is:
If you still need to setup a rule, a simple way to do it is:
```
git clone git@github.com:solokeys/solo.git
@ -22,9 +23,11 @@ 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>.
Or, manually, 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.
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
```
# How do udev rules work and why are they needed

122
fido2/apdu.c Normal file
View File

@ -0,0 +1,122 @@
// 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.
// iso7816:2013. 5.3.2 Decoding conventions for command bodies
#include "apdu.h"
int apdu_decode(uint8_t *data, size_t len, APDU_STRUCT *apdu)
{
EXT_APDU_HEADER *hapdu = (EXT_APDU_HEADER *)data;
apdu->cla = hapdu->cla & 0xef; // mask chaining bit if any
apdu->ins = hapdu->ins;
apdu->p1 = hapdu->p1;
apdu->p2 = hapdu->p2;
apdu->lc = 0;
apdu->data = NULL;
apdu->le = 0;
apdu->extended_apdu = false;
apdu->case_type = 0x00;
uint8_t b0 = hapdu->lc[0];
// case 1
if (len == 4)
{
apdu->case_type = 0x01;
}
// case 2S (Le)
if (len == 5)
{
apdu->case_type = 0x02;
apdu->le = b0;
if (!apdu->le)
apdu->le = 0x100;
}
// case 3S (Lc + data)
if (len == 5U + b0 && b0 != 0)
{
apdu->case_type = 0x03;
apdu->lc = b0;
}
// case 4S (Lc + data + Le)
if (len == 5U + b0 + 1U && b0 != 0)
{
apdu->case_type = 0x04;
apdu->lc = b0;
apdu->le = data[len - 1];
if (!apdu->le)
apdu->le = 0x100;
}
// extended length apdu
if (len >= 7 && b0 == 0)
{
uint16_t extlen = (hapdu->lc[1] << 8) + hapdu->lc[2];
// case 2E (Le) - extended
if (len == 7)
{
apdu->case_type = 0x12;
apdu->extended_apdu = true;
apdu->le = extlen;
if (!apdu->le)
apdu->le = 0x10000;
}
// case 3E (Lc + data) - extended
if (len == 7U + extlen)
{
apdu->case_type = 0x13;
apdu->extended_apdu = true;
apdu->lc = extlen;
}
// case 4E (Lc + data + Le) - extended 2-byte Le
if (len == 7U + extlen + 2U)
{
apdu->case_type = 0x14;
apdu->extended_apdu = true;
apdu->lc = extlen;
apdu->le = (data[len - 2] << 8) + data[len - 1];
if (!apdu->le)
apdu->le = 0x10000;
}
// case 4E (Lc + data + Le) - extended 3-byte Le
if (len == 7U + extlen + 3U && data[len - 3] == 0)
{
apdu->case_type = 0x24;
apdu->extended_apdu = true;
apdu->lc = extlen;
apdu->le = (data[len - 2] << 8) + data[len - 1];
if (!apdu->le)
apdu->le = 0x10000;
}
}
if (!apdu->case_type)
return 1;
if (apdu->lc)
{
if (apdu->extended_apdu)
{
apdu->data = data + 7;
} else {
apdu->data = data + 5;
}
}
return 0;
}

View File

@ -2,6 +2,8 @@
#define _APDU_H_
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
typedef struct
{
@ -12,19 +14,50 @@ typedef struct
uint8_t lc;
} __attribute__((packed)) APDU_HEADER;
typedef struct
{
uint8_t cla;
uint8_t ins;
uint8_t p1;
uint8_t p2;
uint8_t lc[3];
} __attribute__((packed)) EXT_APDU_HEADER;
typedef struct
{
uint8_t cla;
uint8_t ins;
uint8_t p1;
uint8_t p2;
uint16_t lc;
uint8_t *data;
uint32_t le;
bool extended_apdu;
uint8_t case_type;
} __attribute__((packed)) APDU_STRUCT;
extern int apdu_decode(uint8_t *data, size_t len, APDU_STRUCT *apdu);
#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_FIDO_U2F_VENDOR_FIRST 0xc0 // First vendor defined command
#define APDU_FIDO_U2F_VENDOR_LAST 0xff // Last vendor defined command
#define APDU_SOLO_RESET 0xee
#define APDU_INS_SELECT 0xA4
#define APDU_INS_READ_BINARY 0xB0
#define APDU_GET_RESPONSE 0xC0
#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_INCORRECT_P1P2 0x6a86
#define SW_INS_INVALID 0x6d00 // Instruction code not supported or invalid
#define SW_CLA_INVALID 0x6e00
#define SW_INTERNAL_EXCEPTION 0x6f00
#endif //_APDU_H_

View File

@ -262,6 +262,11 @@ void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8
memmove(y,pubkey+32,32);
}
void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey)
{
uECC_compute_public_key(privkey, pubkey, _es256_curve);
}
void crypto_load_external_key(uint8_t * key, int len)
{
_signing_key = key;

View File

@ -26,6 +26,7 @@ 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);
void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey);
void crypto_ecc256_load_key(uint8_t * data, int len, uint8_t * data2, int len2);
void crypto_ecc256_load_attestation_key();
@ -38,6 +39,7 @@ void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, ui
void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey);
void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey, uint8_t * shared_secret);
#define CRYPTO_TRANSPORT_KEY2 ((uint8_t*)2)
#define CRYPTO_TRANSPORT_KEY ((uint8_t*)1)
#define CRYPTO_MASTER_KEY ((uint8_t*)0)

View File

@ -36,16 +36,7 @@ AuthenticatorState STATE;
static void ctap_reset_key_agreement();
static struct {
CTAP_authDataHeader authData;
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE-1];
uint8_t lastcmd;
uint32_t count;
uint32_t index;
uint32_t time;
uint8_t user_verified;
} getAssertionState;
struct _getAssertionState getAssertionState;
uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash)
{
@ -265,7 +256,9 @@ static int ctap_generate_cose_key(CborEncoder * cose_key, uint8_t * hmac_input,
switch(algtype)
{
case COSE_ALG_ES256:
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
crypto_ecc256_derive_public_key(hmac_input, len, x, y);
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
break;
default:
printf2(TAG_ERR,"Error, COSE alg %d not supported\n", algtype);
@ -279,6 +272,7 @@ static int ctap_generate_cose_key(CborEncoder * cose_key, uint8_t * hmac_input,
void make_auth_tag(uint8_t * rpIdHash, uint8_t * nonce, uint32_t count, uint8_t * tag)
{
uint8_t hashbuf[32];
memset(hashbuf,0,sizeof(hashbuf));
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY, 0, hashbuf);
crypto_sha256_update(rpIdHash, 32);
crypto_sha256_update(nonce, CREDENTIAL_NONCE_SIZE);
@ -363,9 +357,9 @@ static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf
}
// Generate credRandom
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY, 0, credRandom);
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId));
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY, 0, credRandom);
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
// Decrypt saltEnc
crypto_aes256_init(shared_secret, NULL);
@ -388,7 +382,6 @@ static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf
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;
@ -436,9 +429,29 @@ static unsigned int get_credential_id_size(CTAP_credentialDescriptor * cred)
{
if (cred->type == PUB_KEY_CRED_CTAP1)
return U2F_KEY_HANDLE_SIZE;
if (cred->type == PUB_KEY_CRED_CUSTOM)
return getAssertionState.customCredIdSize;
return sizeof(CredentialId);
}
static int ctap2_user_presence_test()
{
device_set_status(CTAPHID_STATUS_UPNEEDED);
int ret = ctap_user_presence_test(CTAP2_UP_DELAY_MS);
if ( ret > 0 )
{
return CTAP1_ERR_SUCCESS;
}
else if (ret < 0)
{
return CTAP2_ERR_KEEPALIVE_CANCEL;
}
else
{
return CTAP2_ERR_ACTION_TIMEOUT;
}
}
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;
@ -450,6 +463,10 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
uint8_t * cose_key_buf = auth_data_buf + sizeof(CTAP_authData);
// memset(&cose_key, 0, sizeof(CTAP_residentKey));
memset(&rk, 0, sizeof(CTAP_residentKey));
memset(&rk2, 0, sizeof(CTAP_residentKey));
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));
@ -460,29 +477,16 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
crypto_sha256_update(rp->id, rp->size);
crypto_sha256_final(authData->head.rpIdHash);
printf1(TAG_RED, "rpId: "); dump_hex1(TAG_RED, rp->id, rp->size);
printf1(TAG_RED, "hash: "); dump_hex1(TAG_RED, authData->head.rpIdHash, 32);
count = auth_data_update_count(&authData->head);
device_set_status(CTAPHID_STATUS_UPNEEDED);
int but;
but = ctap_user_presence_test();
but = ctap2_user_presence_test(CTAP2_UP_DELAY_MS);
check_retr(but);
if (!but)
{
return CTAP2_ERR_OPERATION_DENIED;
}
else if (but < 0) // Cancel
{
return CTAP2_ERR_KEEPALIVE_CANCEL;
}
device_set_status(CTAPHID_STATUS_PROCESSING);
authData->head.flags = (but << 0);
authData->head.flags = (1 << 0); // User presence
authData->head.flags |= (ctap_is_pin_set() << 2);
@ -612,7 +616,6 @@ int ctap_calculate_signature(uint8_t * data, int datalen, uint8_t * clientDataHa
crypto_sha256_final(hashbuf);
crypto_ecc256_sign(hashbuf, 32, sigbuf);
return ctap_encode_der_sig(sigbuf,sigder);
}
@ -671,12 +674,17 @@ int ctap_authenticate_credential(struct rpId * rp, CTAP_credentialDescriptor * d
return (memcmp(desc->credential.id.tag, tag, CREDENTIAL_TAG_SIZE) == 0);
break;
case PUB_KEY_CRED_CTAP1:
printf1(TAG_CTAP,"PUB_KEY_CRED_CTAP1\r\n");
crypto_sha256_init();
crypto_sha256_update(rp->id, rp->size);
crypto_sha256_final(rpIdHash);
return u2f_authenticate_credential((struct u2f_key_handle *)&desc->credential.id, rpIdHash);
break;
case PUB_KEY_CRED_CUSTOM:
return is_extension_request(getAssertionState.customCredId, getAssertionState.customCredIdSize);
break;
default:
printf1(TAG_ERR, "PUB_KEY_CRED_UNKNOWN %x\r\n",desc->type);
break;
}
return 0;
@ -703,11 +711,8 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
}
if (MC.pinAuthEmpty)
{
if (!ctap_user_presence_test())
{
return CTAP2_ERR_OPERATION_DENIED;
}
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET;
check_retr( ctap2_user_presence_test(CTAP2_UP_DELAY_MS) );
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
}
if ((MC.paramsParsed & MC_requiredMask) != MC_requiredMask)
{
@ -745,9 +750,7 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
check_retr(ret);
printf1(TAG_GREEN, "checking credId: "); dump_hex1(TAG_GREEN, (uint8_t*) &excl_cred->credential.id, sizeof(CredentialId));
// DELETE
// crypto_aes256_reset_iv(NULL);
// crypto_aes256_decrypt((uint8_t*)& excl_cred->credential.enc, CREDENTIAL_ENC_SIZE);
if (ctap_authenticate_credential(&MC.rp, excl_cred))
{
printf1(TAG_MC, "Cred %d failed!\r\n",i);
@ -868,7 +871,6 @@ uint8_t ctap_add_user_entity(CborEncoder * map, CTAP_userEntity * user)
ret = cbor_encoder_create_map(map, &entity, 1);
check_ret(ret);
printf1(TAG_GREEN,"id_size: %d\r\n", user->id_size);
{
ret = cbor_encode_text_string(&entity, "id", 2);
check_ret(ret);
@ -1061,7 +1063,7 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr
else
#endif
{
sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(CTAP_authDataHeader), clientDataHash, auth_data_buf, sigbuf, sigder);
sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_buf_sz, clientDataHash, auth_data_buf, sigbuf, sigder);
}
{
@ -1110,7 +1112,6 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
}
check_ret(ret);
printf1(TAG_RED, "RPID hash: "); dump_hex1(TAG_RED, authData.rpIdHash, 32);
// if only one account for this RP, null out the user details
if (!getAssertionState.user_verified)
@ -1131,6 +1132,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) + 80];
int ret = ctap_parse_get_assertion(&GA,request,length);
@ -1142,11 +1144,8 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
if (GA.pinAuthEmpty)
{
if (!ctap_user_presence_test())
{
return CTAP2_ERR_OPERATION_DENIED;
}
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET;
check_retr( ctap2_user_presence_test(CTAP2_UP_DELAY_MS) );
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
}
if (GA.pinAuthPresent)
{
@ -1170,16 +1169,21 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
printf1(TAG_GA, "ALLOW_LIST has %d creds\n", GA.credLen);
int validCredCount = ctap_filter_invalid_credentials(&GA);
if (validCredCount > 1)
if (validCredCount == 0)
{
printf2(TAG_ERR,"Error, no authentic credential\n");
return CTAP2_ERR_NO_CREDENTIALS;
}
else if (validCredCount > 1)
{
map_size += 1;
}
if (GA.creds[validCredCount - 1].credential.user.id_size)
{
map_size += 1;
}
if (GA.extensions.hmac_secret_present == EXT_HMAC_SECRET_PARSED)
{
printf1(TAG_GA, "hmac-secret is present\r\n");
@ -1188,12 +1192,6 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
ret = cbor_encoder_create_map(encoder, &map, map_size);
check_ret(ret);
if (validCredCount == 0)
{
printf2(TAG_ERR,"Error, no authentic credential\n");
return CTAP2_ERR_NO_CREDENTIALS;
}
// if only one account for this RP, null out the user details
if (validCredCount < 2 || !getAssertionState.user_verified)
{
@ -1217,8 +1215,14 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
#ifdef ENABLE_U2F_EXTENSIONS
if ( is_extension_request((uint8_t*)&GA.creds[validCredCount - 1].credential.id, sizeof(CredentialId)) )
{
memset(auth_data_buf,0,sizeof(CTAP_authDataHeader));
auth_data_buf_sz = sizeof(CTAP_authDataHeader);
crypto_sha256_init();
crypto_sha256_update(GA.rp.id, GA.rp.size);
crypto_sha256_final(((CTAP_authDataHeader *)auth_data_buf)->rpIdHash);
((CTAP_authDataHeader *)auth_data_buf)->flags = (1 << 0);
((CTAP_authDataHeader *)auth_data_buf)->flags |= (1 << 2);
}
else
#endif
@ -1227,8 +1231,8 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, &auth_data_buf_sz, NULL);
check_retr(ret);
((CTAP_authData *)auth_data_buf)->head.flags &= ~(1 << 2);
((CTAP_authData *)auth_data_buf)->head.flags |= (getAssertionState.user_verified << 2);
((CTAP_authDataHeader *)auth_data_buf)->flags &= ~(1 << 2);
((CTAP_authDataHeader *)auth_data_buf)->flags |= (getAssertionState.user_verified << 2);
{
unsigned int ext_encoder_buf_size = sizeof(auth_data_buf) - auth_data_buf_sz;
@ -1238,7 +1242,7 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
check_retr(ret);
if (ext_encoder_buf_size)
{
((CTAP_authData *)auth_data_buf)->head.flags |= (1 << 7);
((CTAP_authDataHeader *)auth_data_buf)->flags |= (1 << 7);
auth_data_buf_sz += ext_encoder_buf_size;
}
}
@ -1475,6 +1479,11 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length)
ret = cbor_encode_int(&map, RESP_keyAgreement);
check_ret(ret);
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
crypto_ecc256_compute_public_key(KEY_AGREEMENT_PRIV, KEY_AGREEMENT_PUB);
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
ret = ctap_add_cose_key(&map, KEY_AGREEMENT_PUB, KEY_AGREEMENT_PUB+32, PUB_KEY_CRED_PUB_KEY, COSE_ALG_ECDH_ES_HKDF_256);
check_retr(ret);
@ -1571,6 +1580,7 @@ void ctap_response_init(CTAP_RESPONSE * resp)
uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
{
CborEncoder encoder;
memset(&encoder,0,sizeof(CborEncoder));
uint8_t status = 0;
uint8_t cmd = *pkt_raw;
pkt_raw++;
@ -1603,7 +1613,6 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
switch(cmd)
{
case CTAP_MAKE_CREDENTIAL:
device_set_status(CTAPHID_STATUS_PROCESSING);
printf1(TAG_CTAP,"CTAP_MAKE_CREDENTIAL\n");
timestamp();
status = ctap_make_credential(&encoder, pkt_raw, length);
@ -1614,7 +1623,6 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
break;
case CTAP_GET_ASSERTION:
device_set_status(CTAPHID_STATUS_PROCESSING);
printf1(TAG_CTAP,"CTAP_GET_ASSERTION\n");
timestamp();
status = ctap_get_assertion(&encoder, pkt_raw, length);
@ -1646,14 +1654,11 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
break;
case CTAP_RESET:
printf1(TAG_CTAP,"CTAP_RESET\n");
if (ctap_user_presence_test())
status = ctap2_user_presence_test(CTAP2_UP_DELAY_MS);
if (status == CTAP1_ERR_SUCCESS)
{
ctap_reset();
}
else
{
status = CTAP2_ERR_OPERATION_DENIED;
}
break;
case GET_NEXT_ASSERTION:
printf1(TAG_CTAP,"CTAP_NEXT_ASSERTION\n");
@ -1670,13 +1675,12 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
else
{
printf2(TAG_ERR, "unwanted GET_NEXT_ASSERTION. lastcmd == 0x%02x\n", getAssertionState.lastcmd);
dump_hex1(TAG_GREEN, (uint8_t*)&getAssertionState, sizeof(getAssertionState));
status = CTAP2_ERR_NOT_ALLOWED;
}
break;
default:
status = CTAP1_ERR_INVALID_COMMAND;
printf2(TAG_ERR,"error, invalid cmd\n");
printf2(TAG_ERR,"error, invalid cmd: 0x%02x\n", cmd);
}
done:
@ -1765,10 +1769,7 @@ void ctap_init()
exit(1);
}
if (! device_is_nfc())
{
ctap_reset_key_agreement();
}
#ifdef BRIDGE_TO_WALLET
wallet_init();
@ -1967,7 +1968,7 @@ int8_t ctap_load_key(uint8_t index, uint8_t * key)
static void ctap_reset_key_agreement()
{
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
ctap_generate_rng(KEY_AGREEMENT_PRIV, sizeof(KEY_AGREEMENT_PRIV));
}
void ctap_reset()
@ -1987,5 +1988,5 @@ void ctap_reset()
memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH));
ctap_reset_key_agreement();
crypto_reset_master_secret();
crypto_load_master_secret(STATE.key_space);
}

View File

@ -113,6 +113,7 @@
#define PUB_KEY_CRED_PUB_KEY 0x01
#define PUB_KEY_CRED_CTAP1 0x41
#define PUB_KEY_CRED_CUSTOM 0x42
#define PUB_KEY_CRED_UNKNOWN 0x3F
#define CREDENTIAL_IS_SUPPORTED 1
@ -130,6 +131,8 @@
#define PIN_LOCKOUT_ATTEMPTS 8 // Number of attempts total
#define PIN_BOOT_ATTEMPTS 3 // number of attempts per boot
#define CTAP2_UP_DELAY_MS 29000
typedef struct
{
uint8_t id[USER_ID_MAX_SIZE];
@ -278,7 +281,7 @@ typedef struct
uint8_t pinAuthEmpty;
int pinProtocol;
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
CTAP_credentialDescriptor * creds;
uint8_t allowListPresent;
CTAP_extensions extensions;
@ -302,6 +305,19 @@ typedef struct
} CTAP_clientPin;
struct _getAssertionState {
CTAP_authDataHeader authData;
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
uint8_t lastcmd;
uint32_t count;
uint32_t index;
uint32_t time;
uint8_t user_verified;
uint8_t customCredId[256];
uint8_t customCredIdSize;
};
void ctap_response_init(CTAP_RESPONSE * resp);
uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp);

View File

@ -49,6 +49,7 @@
#define CTAP2_ERR_PIN_POLICY_VIOLATION 0x37
#define CTAP2_ERR_PIN_TOKEN_EXPIRED 0x38
#define CTAP2_ERR_REQUEST_TOO_LARGE 0x39
#define CTAP2_ERR_ACTION_TIMEOUT 0x3A
#define CTAP1_ERR_OTHER 0x7F
#define CTAP2_ERR_SPEC_LAST 0xDF
#define CTAP2_ERR_EXTENSION_FIRST 0xE0

View File

@ -16,6 +16,7 @@
#include "util.h"
#include "log.h"
extern struct _getAssertionState getAssertionState;
void _check_ret(CborError ret, int line, const char * filename)
{
@ -136,7 +137,6 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
return CTAP2_ERR_LIMIT_EXCEEDED;
}
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)
@ -883,6 +883,8 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
size_t buflen;
char type[12];
CborValue val;
cred->type = 0;
if (cbor_value_get_type(arr) != CborMapType)
{
printf2(TAG_ERR,"Error, CborMapType expected in credential\n");
@ -899,7 +901,7 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
}
buflen = sizeof(CredentialId);
cbor_value_copy_byte_string(&val, (uint8_t*)&cred->credential.id, &buflen, NULL);
ret = cbor_value_copy_byte_string(&val, (uint8_t*)&cred->credential.id, &buflen, NULL);
if (buflen == U2F_KEY_HANDLE_SIZE)
{
@ -908,8 +910,13 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
}
else if (buflen != sizeof(CredentialId))
{
printf2(TAG_ERR,"Ignoring credential is incorrect length\n");
printf2(TAG_ERR,"Ignoring credential is incorrect length, treating as custom\n");
cred->type = PUB_KEY_CRED_CUSTOM;
buflen = 256;
ret = cbor_value_copy_byte_string(&val, getAssertionState.customCredId, &buflen, NULL);
getAssertionState.customCredIdSize = buflen;
}
check_ret(ret);
ret = cbor_value_map_find_value(arr, "type", &val);
check_ret(ret);
@ -922,11 +929,19 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
buflen = sizeof(type);
ret = cbor_value_copy_text_string(&val, type, &buflen, NULL);
if (ret == CborErrorOutOfMemory)
{
cred->type = PUB_KEY_CRED_UNKNOWN;
}
else
{
check_ret(ret);
}
if (strncmp(type, "public-key",11) == 0)
{
if (PUB_KEY_CRED_CTAP1 != cred->type)
if (0 == cred->type)
{
cred->type = PUB_KEY_CRED_PUB_KEY;
}
@ -994,6 +1009,8 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
CborValue it,map;
memset(GA, 0, sizeof(CTAP_getAssertion));
GA->creds = getAssertionState.creds; // Save stack memory
ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
check_ret(ret);

View File

@ -9,7 +9,7 @@
#include "storage.h"
void device_init();
void device_init(int argc, char *argv[]);
uint32_t millis();
@ -30,6 +30,7 @@ void main_loop_delay();
void heartbeat();
void device_reboot();
void authenticator_read_state(AuthenticatorState * );
@ -53,11 +54,11 @@ int device_is_button_pressed();
// Test for user presence
// Return 1 for user is present, 0 user not present, -1 if cancel is requested.
extern int ctap_user_presence_test();
int ctap_user_presence_test(uint32_t delay);
// Generate @num bytes of random numbers to @dest
// return 1 if success, error otherwise
extern int ctap_generate_rng(uint8_t * dst, size_t num);
int ctap_generate_rng(uint8_t * dst, size_t num);
// Increment atomic counter and return it.
// Must support two counters, @sel selects counter0 or counter1.
@ -65,11 +66,11 @@ uint32_t ctap_atomic_count(int sel);
// Verify the user
// return 1 if user is verified, 0 if not
extern int ctap_user_verification(uint8_t arg);
int ctap_user_verification(uint8_t arg);
// Must be implemented by application
// data is HID_MESSAGE_SIZE long in bytes
extern void ctaphid_write_block(uint8_t * data);
void ctaphid_write_block(uint8_t * data);
// Resident key
@ -99,9 +100,14 @@ typedef enum {
// 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();
// Returns NFC_IS_NA, NFC_IS_ACTIVE, or NFC_IS_AVAILABLE
#define NFC_IS_NA 0
#define NFC_IS_ACTIVE 1
#define NFC_IS_AVAILABLE 2
int device_is_nfc();
void request_from_nfc(bool request_active);
void device_init_button();
#endif

View File

@ -35,6 +35,28 @@ int extension_needs_atomic_count(uint8_t klen, uint8_t * keyh)
|| ((wallet_request *) keyh)->operation == WalletSign;
}
static uint8_t * output_buffer_ptr;
uint8_t output_buffer_offset;
uint8_t output_buffer_size;
void extension_writeback_init(uint8_t * buffer, uint8_t size)
{
output_buffer_ptr = buffer;
output_buffer_offset = 0;
output_buffer_size = size;
}
void extension_writeback(uint8_t * buf, uint8_t size)
{
if ((output_buffer_offset + size) > output_buffer_size)
{
return;
}
memmove(output_buffer_ptr + output_buffer_offset, buf, size);
output_buffer_offset += size;
}
int16_t bridge_u2f_to_extensions(uint8_t * _chal, uint8_t * _appid, uint8_t klen, uint8_t * keyh)
{
int8_t ret = 0;
@ -55,8 +77,6 @@ int16_t bridge_u2f_to_extensions(uint8_t * _chal, uint8_t * _appid, uint8_t klen
u2f_response_writeback((uint8_t *)&ret,1);
#ifdef IS_BOOTLOADER
ret = bootloader_bridge(klen, keyh);
#elif defined(WALLET_EXTENSION)
ret = bridge_u2f_to_wallet(_chal, _appid, klen, keyh);
#else
ret = bridge_u2f_to_solo(sig, keyh, klen);
u2f_response_writeback(sig,72);
@ -82,6 +102,7 @@ int16_t extend_fido2(CredentialId * credid, uint8_t * output)
{
if (is_extension_request((uint8_t*)credid, sizeof(CredentialId)))
{
printf1(TAG_EXT,"IS EXT REQ\r\n");
output[0] = bridge_u2f_to_solo(output+1, (uint8_t*)credid, sizeof(CredentialId));
return 1;
}

View File

@ -9,6 +9,11 @@
#include "u2f.h"
#include "apdu.h"
int16_t bridge_u2f_to_extensions(uint8_t * chal, uint8_t * appid, uint8_t klen, uint8_t * keyh);
// return 1 if request is a wallet request
int is_extension_request(uint8_t * req, int len);
int16_t extend_u2f(APDU_HEADER * req, uint8_t * payload, uint32_t len);
int16_t extend_fido2(CredentialId * credid, uint8_t * output);
@ -17,4 +22,8 @@ int bootloader_bridge(int klen, uint8_t * keyh);
int is_extension_request(uint8_t * kh, int len);
void extension_writeback_init(uint8_t * buffer, uint8_t size);
void extension_writeback(uint8_t * buf, uint8_t size);
#endif /* EXTENSIONS_H_ */

View File

@ -31,12 +31,15 @@
#include "log.h"
#include APP_CONFIG
// output must be at least 71 bytes
int16_t bridge_u2f_to_solo(uint8_t * output, uint8_t * keyh, int keylen)
{
int8_t ret = 0;
wallet_request * req = (wallet_request *) keyh;
extension_writeback_init(output, 71);
printf1(TAG_WALLET, "u2f-solo [%d]: ", keylen); dump_hex1(TAG_WALLET, keyh, keylen);
@ -61,6 +64,14 @@ int16_t bridge_u2f_to_solo(uint8_t * output, uint8_t * keyh, int keylen)
break;
#ifdef ENABLE_WALLET
case WalletSign:
case WalletRegister:
case WalletPin:
case WalletReset:
return bridge_to_wallet(keyh, keylen);
#endif
default:
printf2(TAG_ERR,"Invalid wallet command: %x\n",req->operation);
ret = CTAP1_ERR_INVALID_COMMAND;

View File

@ -14,8 +14,8 @@
#include "util.h"
#include "storage.h"
#include "device.h"
#include "extensions.h"
#if defined(USING_PC) || defined(IS_BOOTLOADER)
typedef enum
{
MBEDTLS_ECP_DP_NONE = 0,
@ -32,9 +32,7 @@ typedef enum
MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */
MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */
} mbedtls_ecp_group_id;
#else
#include "ecp.h"
#endif
// return 1 if hash is valid, 0 otherwise
@ -70,14 +68,14 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a
return CTAP2_ERR_NOT_ALLOWED;
}
u2f_response_writeback(KEY_AGREEMENT_PUB,sizeof(KEY_AGREEMENT_PUB));
extension_writeback(KEY_AGREEMENT_PUB,sizeof(KEY_AGREEMENT_PUB));
printf1(TAG_WALLET,"pubkey: "); dump_hex1(TAG_WALLET,KEY_AGREEMENT_PUB,64);
break;
case CP_cmdGetRetries:
printf1(TAG_WALLET,"cmdGetRetries\n");
pinTokenEnc[0] = ctap_leftover_pin_attempts();
u2f_response_writeback(pinTokenEnc,1);
extension_writeback(pinTokenEnc,1);
break;
case CP_cmdSetPin:
@ -87,7 +85,7 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a
return CTAP2_ERR_NOT_ALLOWED;
}
if (!ctap_user_presence_test())
if (!ctap_user_presence_test(5000))
{
return CTAP2_ERR_OPERATION_DENIED;
}
@ -113,7 +111,7 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a
return CTAP2_ERR_NOT_ALLOWED;
}
if (!ctap_user_presence_test())
if (!ctap_user_presence_test(5000))
{
return CTAP2_ERR_OPERATION_DENIED;
}
@ -135,7 +133,7 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a
return CTAP2_ERR_NOT_ALLOWED;
}
if (!ctap_user_presence_test())
if (!ctap_user_presence_test(5000))
{
return CTAP2_ERR_OPERATION_DENIED;
}
@ -145,7 +143,7 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a
return ret;
printf1(TAG_WALLET,"pinToken: "); dump_hex1(TAG_WALLET, PIN_TOKEN, 16);
u2f_response_writeback(pinTokenEnc, PIN_TOKEN_SIZE);
extension_writeback(pinTokenEnc, PIN_TOKEN_SIZE);
break;
@ -159,7 +157,7 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a
return 0;
}
int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, uint8_t * keyh)
int16_t bridge_to_wallet(uint8_t * keyh, uint8_t klen)
{
static uint8_t msg_buf[WALLET_MAX_BUFFER];
int reqlen = klen;
@ -259,7 +257,7 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
crypto_load_external_key(key, keysize);
crypto_ecdsa_sign(args[0], lens[0], sig, MBEDTLS_ECP_DP_SECP256K1);
u2f_response_writeback(sig,64);
extension_writeback(sig,64);
break;
case WalletRegister:
@ -361,7 +359,7 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
}
}
if (ctap_user_presence_test())
if (ctap_user_presence_test(5000))
{
printf1(TAG_WALLET,"Reseting device!\n");
ctap_reset();
@ -374,39 +372,7 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
break;
case WalletVersion:
u2f_response_writeback((uint8_t*)WALLET_VERSION, sizeof(WALLET_VERSION)-1);
break;
case WalletRng:
printf1(TAG_WALLET,"WalletRng\n");
if ( ctap_device_locked() )
{
printf1(TAG_ERR,"device locked\n");
ret = CTAP2_ERR_NOT_ALLOWED;
goto cleanup;
}
if ( ctap_is_pin_set() )
{
if ( ! check_pinhash(req->pinAuth, msg_buf, reqlen))
{
printf2(TAG_ERR,"pinAuth is NOT valid\n");
dump_hex1(TAG_ERR,msg_buf,reqlen);
ret = CTAP2_ERR_PIN_AUTH_INVALID;
goto cleanup;
}
}
ret = ctap_generate_rng(sig, 72);
if (ret != 1)
{
printf1(TAG_WALLET,"Rng failed\n");
ret = CTAP2_ERR_PROCESSING;
goto cleanup;
}
ret = 0;
u2f_response_writeback((uint8_t *)sig,72);
break;
default:
printf2(TAG_ERR,"Invalid wallet command: %x\n",req->operation);

View File

@ -87,10 +87,7 @@ typedef enum
} WalletOperation;
int16_t bridge_u2f_to_extensions(uint8_t * chal, uint8_t * appid, uint8_t klen, uint8_t * keyh);
// return 1 if request is a wallet request
int is_extension_request(uint8_t * req, int len);
int16_t bridge_to_wallet(uint8_t * keyh, uint8_t klen);
void wallet_init();

View File

@ -7,6 +7,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include "cbor.h"
#include "device.h"
@ -19,7 +21,8 @@
#if !defined(TEST)
int main()
int main(int argc, char *argv[])
{
uint8_t hidmsg[64];
uint32_t t1 = 0;
@ -27,32 +30,29 @@ int main()
set_logging_mask(
/*0*/
//TAG_GEN|
//TAG_MC |
//TAG_GA |
//TAG_WALLET |
// TAG_MC |
// TAG_GA |
TAG_WALLET |
TAG_STOR |
//TAG_NFC_APDU |
TAG_NFC |
//TAG_CP |
//TAG_CTAP|
// TAG_CTAP|
//TAG_HID|
//TAG_U2F|
TAG_U2F|
//TAG_PARSE |
//TAG_TIME|
//TAG_DUMP|
// TAG_DUMP|
TAG_GREEN|
TAG_RED|
TAG_EXT|
TAG_ERR
);
device_init();
device_init(argc, argv);
memset(hidmsg,0,sizeof(hidmsg));
// printf1(TAG_GEN,"recv'ing hid msg \n");
while(1)
{

View File

@ -7,6 +7,7 @@
#include <stdlib.h>
#include "u2f.h"
#include "ctap.h"
#include "ctaphid.h"
#include "crypto.h"
#include "log.h"
#include "device.h"
@ -95,6 +96,8 @@ void u2f_request_ex(APDU_HEADER *req, uint8_t *payload, uint32_t len, CTAP_RESPO
#endif
}
device_set_status(CTAPHID_STATUS_IDLE);
end:
if (rcode != U2F_SW_NO_ERROR)
{
@ -110,14 +113,14 @@ 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)
void u2f_request_nfc(uint8_t * header, uint8_t * data, int datalen, CTAP_RESPONSE * resp)
{
if (len < 5 || !req)
if (!header)
return;
uint32_t alen = req[4];
u2f_request_ex((APDU_HEADER *)req, &req[5], alen, resp);
request_from_nfc(true); // disable presence test
u2f_request_ex((APDU_HEADER *)header, data, datalen, resp);
request_from_nfc(false); // enable presence test
}
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
@ -202,7 +205,6 @@ int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t * appid)
}
static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t control)
{
@ -238,9 +240,9 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
if (control == U2F_AUTHENTICATE_SIGN_NO_USER)
up = 0;
if(!device_is_nfc() && up)
if(up)
{
if (ctap_user_presence_test() == 0)
if (ctap_user_presence_test(750) == 0)
{
return U2F_SW_CONDITIONS_NOT_SATISFIED;
}
@ -286,13 +288,10 @@ 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())
if ( ! ctap_user_presence_test(750))
{
return U2F_SW_CONDITIONS_NOT_SATISFIED;
}
}
if ( u2f_new_keypair(&key_handle, req->app, pubkey) == -1)
{
@ -326,8 +325,6 @@ static int16_t u2f_register(struct u2f_register_request * req)
dump_signature_der(sig);
/*printf1(TAG_U2F, "dersig: "); dump_hex1(TAG_U2F,sig,74);*/
return U2F_SW_NO_ERROR;
}

View File

@ -101,7 +101,7 @@ 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);
void u2f_request_nfc(uint8_t * header, uint8_t * data, int datalen, CTAP_RESPONSE * resp);
int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t * appid);

View File

@ -38,6 +38,7 @@ build firmware hacker solo
build firmware hacker-debug-1 solo
build firmware hacker-debug-2 solo
build firmware secure solo
build firmware secure-non-solokeys solo
pip install -U pip
pip install -U solo-python
@ -49,3 +50,6 @@ 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
bundle="bundle-secure-non-solokeys-${version}"
/opt/conda/bin/solo mergehex bootloader-verifying-${version}.hex firmware-secure-non-solokeys-${version}.hex ${bundle}.hex
sha256sum ${bundle}.hex > ${bundle}.sha2

View File

@ -20,6 +20,9 @@
],
"userVerificationDetails": [
[
{
"userVerification": 1
},
{
"userVerification": 4
}

View File

@ -11,6 +11,11 @@ nav:
- FIDO2 Implementation: solo/fido2-impl.md
- Metadata Statements: solo/metadata-statements.md
- Build instructions: solo/building.md
- Programming instructions: solo/programming.md
- Bootloader mode: solo/bootloader-mode.md
- Customization: solo/customization.md
- Solo Extras: solo/solo-extras.md
- Running on Nucleo32 board: solo/nucleo32-board.md
- Signed update process: solo/signed-updates.md
- Code documentation: solo/code-overview.md
- Contributing Code: solo/contributing.md

View File

@ -7,6 +7,7 @@
#ifndef SRC_APP_H_
#define SRC_APP_H_
#include <stdbool.h>
#define USING_DEV_BOARD
@ -20,6 +21,8 @@
void printing_init();
extern bool use_udp;
// 0xRRGGBB
#define LED_INIT_VALUE 0x000800
#define LED_WINK_VALUE 0x000008

View File

@ -15,6 +15,7 @@
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include "device.h"
#include "cbor.h"
@ -24,6 +25,8 @@
#define RK_NUM 50
bool use_udp = true;
struct ResidentKeyStore {
CTAP_residentKey rks[RK_NUM];
} RK_STORE;
@ -40,7 +43,11 @@ void device_set_status(uint32_t status)
__device_status = status;
}
void device_reboot()
{
printf1(TAG_RED, "REBOOT command recieved!\r\n");
exit(100);
}
int udp_server()
{
@ -118,12 +125,6 @@ void udp_send(int fd, uint8_t * buf, int size)
}
}
void udp_close(int fd)
{
close(fd);
}
uint32_t millis()
{
@ -134,18 +135,42 @@ uint32_t millis()
}
static int serverfd = 0;
static int fd = 0;
void usbhid_init()
{
// just bridge to UDP for now for pure software testing
serverfd = udp_server();
if (use_udp)
{
fd = udp_server();
}
else
{
fd = open("/dev/hidg0", O_RDWR);
if (fd < 0)
{
perror("hidg open");
exit(1);
}
}
}
// Receive 64 byte USB HID message, don't block, return size of packet, return 0 if nothing
int usbhid_recv(uint8_t * msg)
{
int l = udp_recv(serverfd, msg, HID_MESSAGE_SIZE);
int l = 0;
if (use_udp)
{
l = udp_recv(fd, msg, HID_MESSAGE_SIZE);
}
else
{
l = read(fd, msg, HID_MESSAGE_SIZE); /* Flawfinder: ignore */
if (l < 0)
{
perror("hidg read");
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"
@ -166,12 +191,23 @@ int usbhid_recv(uint8_t * msg)
// Send 64 byte USB HID message
void usbhid_send(uint8_t * msg)
{
udp_send(serverfd, msg, HID_MESSAGE_SIZE);
if (use_udp)
{
udp_send(fd, msg, HID_MESSAGE_SIZE);
}
else
{
if (write(fd, msg, HID_MESSAGE_SIZE) < 0)
{
perror("hidg write");
exit(1);
}
}
}
void usbhid_close()
{
udp_close(serverfd);
close(fd);
}
void int_handler(int i)
@ -181,10 +217,47 @@ void int_handler(int i)
exit(0);
}
void device_init()
void usage(const char * cmd)
{
fprintf(stderr, "Usage: %s [-b udp|hidg]\n", cmd);
fprintf(stderr, " -b backing implementation: udp(default) or hidg\n");
exit(1);
}
void device_init(int argc, char *argv[])
{
int opt;
while ((opt = getopt(argc, argv, "b:")) != -1)
{
switch (opt)
{
case 'b':
if (strcmp("udp", optarg) == 0)
{
use_udp = true;
}
else if (strcmp("hidg", optarg) == 0)
{
use_udp = false;
}
else
{
usage(argv[0]);
}
break;
default:
usage(argv[0]);
break;
}
}
signal(SIGINT, int_handler);
printf1(TAG_GREEN, "Using %s backing\n", use_udp ? "UDP" : "hidg");
usbhid_init();
authenticator_initialize();
@ -203,6 +276,14 @@ void main_loop_delay()
nanosleep(&ts,NULL);
}
void delay(uint32_t ms)
{
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 1000*1000*ms;
nanosleep(&ts,NULL);
}
void heartbeat()
{
@ -216,7 +297,7 @@ void ctaphid_write_block(uint8_t * data)
}
int ctap_user_presence_test()
int ctap_user_presence_test(uint32_t d)
{
return 1;
}
@ -547,7 +628,12 @@ void device_wink()
printf("*WINK*\n");
}
bool device_is_nfc()
int device_is_nfc()
{
return 0;
}
void device_set_clock_rate(DEVICE_CLOCK_RATE param)
{
}

View File

@ -21,6 +21,9 @@ firmware-hacker-debug-1:
firmware-hacker-debug-2:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
firmware-secure-non-solokeys:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DFLASH_ROP=2'
firmware-secure:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DUSE_SOLOKEYS_CERT -DFLASH_ROP=2'
@ -59,7 +62,6 @@ boot-no-sig:
build-release-locked: cbor clean2 boot-sig-checking clean all-locked
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
rm -f solo.hex bootloader.hex # don't program solo.hex ...
build-release: cbor clean2 boot-sig-checking clean all
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex

View File

@ -83,6 +83,8 @@ int main()
init_debug_uart();
#endif
device_init_button();
printf1(TAG_GEN,"init device\n");
t1 = millis();

View File

@ -2,15 +2,16 @@ 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/nfc.c src/ams.c
SRC += src/fifo.c src/crypto.c src/attestation.c src/nfc.c src/ams.c src/sense.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(DRIVER_LIBS) $(USB_LIB)
# FIDO2 lib
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c
SRC += ../../fido2/apdu.c ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c
SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
SRC += ../../fido2/ctap_parse.c ../../fido2/main.c
SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c
SRC += ../../fido2/extensions/wallet.c
# Crypto libs
SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c ../../crypto/tiny-AES-c/aes.c

View File

@ -3,7 +3,7 @@ include build/common.mk
# ST related
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/fifo.c src/crypto.c src/attestation.c src/sense.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(DRIVER_LIBS) $(USB_LIB)

View File

@ -6,7 +6,7 @@ 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
lib/stm32l4xx_ll_usart.c lib/stm32l4xx_ll_spi.c lib/stm32l4xx_ll_exti.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 \

View File

@ -0,0 +1,844 @@
/**
******************************************************************************
* @file stm32l4xx_hal_tsc.h
* @author MCD Application Team
* @brief Header file of TSC HAL module.
******************************************************************************
* @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.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef STM32L4xx_HAL_TSC_H
#define STM32L4xx_HAL_TSC_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32l4xx_hal_def.h"
/** @addtogroup STM32L4xx_HAL_Driver
* @{
*/
/** @addtogroup TSC
* @{
*/
/* Exported types ------------------------------------------------------------*/
/** @defgroup TSC_Exported_Types TSC Exported Types
* @{
*/
/**
* @brief TSC state structure definition
*/
typedef enum
{
HAL_TSC_STATE_RESET = 0x00UL, /*!< TSC registers have their reset value */
HAL_TSC_STATE_READY = 0x01UL, /*!< TSC registers are initialized or acquisition is completed with success */
HAL_TSC_STATE_BUSY = 0x02UL, /*!< TSC initialization or acquisition is on-going */
HAL_TSC_STATE_ERROR = 0x03UL /*!< Acquisition is completed with max count error */
} HAL_TSC_StateTypeDef;
/**
* @brief TSC group status structure definition
*/
typedef enum
{
TSC_GROUP_ONGOING = 0x00UL, /*!< Acquisition on group is on-going or not started */
TSC_GROUP_COMPLETED = 0x01UL /*!< Acquisition on group is completed with success (no max count error) */
} TSC_GroupStatusTypeDef;
/**
* @brief TSC init structure definition
*/
typedef struct
{
uint32_t CTPulseHighLength; /*!< Charge-transfer high pulse length
This parameter can be a value of @ref TSC_CTPulseHL_Config */
uint32_t CTPulseLowLength; /*!< Charge-transfer low pulse length
This parameter can be a value of @ref TSC_CTPulseLL_Config */
uint32_t SpreadSpectrum; /*!< Spread spectrum activation
This parameter can be a value of @ref TSC_CTPulseLL_Config */
uint32_t SpreadSpectrumDeviation; /*!< Spread spectrum deviation
This parameter must be a number between Min_Data = 0 and Max_Data = 127 */
uint32_t SpreadSpectrumPrescaler; /*!< Spread spectrum prescaler
This parameter can be a value of @ref TSC_SpreadSpec_Prescaler */
uint32_t PulseGeneratorPrescaler; /*!< Pulse generator prescaler
This parameter can be a value of @ref TSC_PulseGenerator_Prescaler */
uint32_t MaxCountValue; /*!< Max count value
This parameter can be a value of @ref TSC_MaxCount_Value */
uint32_t IODefaultMode; /*!< IO default mode
This parameter can be a value of @ref TSC_IO_Default_Mode */
uint32_t SynchroPinPolarity; /*!< Synchro pin polarity
This parameter can be a value of @ref TSC_Synchro_Pin_Polarity */
uint32_t AcquisitionMode; /*!< Acquisition mode
This parameter can be a value of @ref TSC_Acquisition_Mode */
uint32_t MaxCountInterrupt; /*!< Max count interrupt activation
This parameter can be set to ENABLE or DISABLE. */
uint32_t ChannelIOs; /*!< Channel IOs mask */
uint32_t ShieldIOs; /*!< Shield IOs mask */
uint32_t SamplingIOs; /*!< Sampling IOs mask */
} TSC_InitTypeDef;
/**
* @brief TSC IOs configuration structure definition
*/
typedef struct
{
uint32_t ChannelIOs; /*!< Channel IOs mask */
uint32_t ShieldIOs; /*!< Shield IOs mask */
uint32_t SamplingIOs; /*!< Sampling IOs mask */
} TSC_IOConfigTypeDef;
/**
* @brief TSC handle Structure definition
*/
typedef struct __TSC_HandleTypeDef
{
TSC_TypeDef *Instance; /*!< Register base address */
TSC_InitTypeDef Init; /*!< Initialization parameters */
__IO HAL_TSC_StateTypeDef State; /*!< Peripheral state */
HAL_LockTypeDef Lock; /*!< Lock feature */
__IO uint32_t ErrorCode; /*!< I2C Error code */
#if (USE_HAL_TSC_REGISTER_CALLBACKS == 1)
void (* ConvCpltCallback)(struct __TSC_HandleTypeDef *htsc); /*!< TSC Conversion complete callback */
void (* ErrorCallback)(struct __TSC_HandleTypeDef *htsc); /*!< TSC Error callback */
void (* MspInitCallback)(struct __TSC_HandleTypeDef *htsc); /*!< TSC Msp Init callback */
void (* MspDeInitCallback)(struct __TSC_HandleTypeDef *htsc); /*!< TSC Msp DeInit callback */
#endif /* USE_HAL_TSC_REGISTER_CALLBACKS */
} TSC_HandleTypeDef;
/**
* @brief TSC Group Index Structure definition
*/
typedef enum
{
TSC_GROUP1_IDX = 0x00UL,
TSC_GROUP2_IDX,
TSC_GROUP3_IDX,
TSC_GROUP4_IDX,
#if defined(TSC_IOCCR_G5_IO1)
TSC_GROUP5_IDX,
#endif
#if defined(TSC_IOCCR_G6_IO1)
TSC_GROUP6_IDX,
#endif
#if defined(TSC_IOCCR_G7_IO1)
TSC_GROUP7_IDX,
#endif
#if defined(TSC_IOCCR_G8_IO1)
TSC_GROUP8_IDX,
#endif
TSC_NB_OF_GROUPS
}TSC_GroupIndexTypeDef;
#if (USE_HAL_TSC_REGISTER_CALLBACKS == 1)
/**
* @brief HAL TSC Callback ID enumeration definition
*/
typedef enum
{
HAL_TSC_CONV_COMPLETE_CB_ID = 0x00UL, /*!< TSC Conversion completed callback ID */
HAL_TSC_ERROR_CB_ID = 0x01UL, /*!< TSC Error callback ID */
HAL_TSC_MSPINIT_CB_ID = 0x02UL, /*!< TSC Msp Init callback ID */
HAL_TSC_MSPDEINIT_CB_ID = 0x03UL /*!< TSC Msp DeInit callback ID */
} HAL_TSC_CallbackIDTypeDef;
/**
* @brief HAL TSC Callback pointer definition
*/
typedef void (*pTSC_CallbackTypeDef)(TSC_HandleTypeDef *htsc); /*!< pointer to an TSC callback function */
#endif /* USE_HAL_TSC_REGISTER_CALLBACKS */
/**
* @}
*/
/* Exported constants --------------------------------------------------------*/
/** @defgroup TSC_Exported_Constants TSC Exported Constants
* @{
*/
/** @defgroup TSC_Error_Code_definition TSC Error Code definition
* @brief TSC Error Code definition
* @{
*/
#define HAL_TSC_ERROR_NONE 0x00000000UL /*!< No error */
#if (USE_HAL_TSC_REGISTER_CALLBACKS == 1)
#define HAL_TSC_ERROR_INVALID_CALLBACK 0x00000001UL /*!< Invalid Callback error */
#endif /* USE_HAL_TSC_REGISTER_CALLBACKS */
/**
* @}
*/
/** @defgroup TSC_CTPulseHL_Config CTPulse High Length
* @{
*/
#define TSC_CTPH_1CYCLE 0x00000000UL /*!< Charge transfer pulse high during 1 cycle (PGCLK) */
#define TSC_CTPH_2CYCLES TSC_CR_CTPH_0 /*!< Charge transfer pulse high during 2 cycles (PGCLK) */
#define TSC_CTPH_3CYCLES TSC_CR_CTPH_1 /*!< Charge transfer pulse high during 3 cycles (PGCLK) */
#define TSC_CTPH_4CYCLES (TSC_CR_CTPH_1 | TSC_CR_CTPH_0) /*!< Charge transfer pulse high during 4 cycles (PGCLK) */
#define TSC_CTPH_5CYCLES TSC_CR_CTPH_2 /*!< Charge transfer pulse high during 5 cycles (PGCLK) */
#define TSC_CTPH_6CYCLES (TSC_CR_CTPH_2 | TSC_CR_CTPH_0) /*!< Charge transfer pulse high during 6 cycles (PGCLK) */
#define TSC_CTPH_7CYCLES (TSC_CR_CTPH_2 | TSC_CR_CTPH_1) /*!< Charge transfer pulse high during 7 cycles (PGCLK) */
#define TSC_CTPH_8CYCLES (TSC_CR_CTPH_2 | TSC_CR_CTPH_1 | TSC_CR_CTPH_0) /*!< Charge transfer pulse high during 8 cycles (PGCLK) */
#define TSC_CTPH_9CYCLES TSC_CR_CTPH_3 /*!< Charge transfer pulse high during 9 cycles (PGCLK) */
#define TSC_CTPH_10CYCLES (TSC_CR_CTPH_3 | TSC_CR_CTPH_0) /*!< Charge transfer pulse high during 10 cycles (PGCLK) */
#define TSC_CTPH_11CYCLES (TSC_CR_CTPH_3 | TSC_CR_CTPH_1) /*!< Charge transfer pulse high during 11 cycles (PGCLK) */
#define TSC_CTPH_12CYCLES (TSC_CR_CTPH_3 | TSC_CR_CTPH_1 | TSC_CR_CTPH_0) /*!< Charge transfer pulse high during 12 cycles (PGCLK) */
#define TSC_CTPH_13CYCLES (TSC_CR_CTPH_3 | TSC_CR_CTPH_2) /*!< Charge transfer pulse high during 13 cycles (PGCLK) */
#define TSC_CTPH_14CYCLES (TSC_CR_CTPH_3 | TSC_CR_CTPH_2 | TSC_CR_CTPH_0) /*!< Charge transfer pulse high during 14 cycles (PGCLK) */
#define TSC_CTPH_15CYCLES (TSC_CR_CTPH_3 | TSC_CR_CTPH_2 | TSC_CR_CTPH_1) /*!< Charge transfer pulse high during 15 cycles (PGCLK) */
#define TSC_CTPH_16CYCLES (TSC_CR_CTPH_3 | TSC_CR_CTPH_2 | TSC_CR_CTPH_1 | TSC_CR_CTPH_0) /*!< Charge transfer pulse high during 16 cycles (PGCLK) */
/**
* @}
*/
/** @defgroup TSC_CTPulseLL_Config CTPulse Low Length
* @{
*/
#define TSC_CTPL_1CYCLE 0x00000000UL /*!< Charge transfer pulse low during 1 cycle (PGCLK) */
#define TSC_CTPL_2CYCLES TSC_CR_CTPL_0 /*!< Charge transfer pulse low during 2 cycles (PGCLK) */
#define TSC_CTPL_3CYCLES TSC_CR_CTPL_1 /*!< Charge transfer pulse low during 3 cycles (PGCLK) */
#define TSC_CTPL_4CYCLES (TSC_CR_CTPL_1 | TSC_CR_CTPL_0) /*!< Charge transfer pulse low during 4 cycles (PGCLK) */
#define TSC_CTPL_5CYCLES TSC_CR_CTPL_2 /*!< Charge transfer pulse low during 5 cycles (PGCLK) */
#define TSC_CTPL_6CYCLES (TSC_CR_CTPL_2 | TSC_CR_CTPL_0) /*!< Charge transfer pulse low during 6 cycles (PGCLK) */
#define TSC_CTPL_7CYCLES (TSC_CR_CTPL_2 | TSC_CR_CTPL_1) /*!< Charge transfer pulse low during 7 cycles (PGCLK) */
#define TSC_CTPL_8CYCLES (TSC_CR_CTPL_2 | TSC_CR_CTPL_1 | TSC_CR_CTPL_0) /*!< Charge transfer pulse low during 8 cycles (PGCLK) */
#define TSC_CTPL_9CYCLES TSC_CR_CTPL_3 /*!< Charge transfer pulse low during 9 cycles (PGCLK) */
#define TSC_CTPL_10CYCLES (TSC_CR_CTPL_3 | TSC_CR_CTPL_0) /*!< Charge transfer pulse low during 10 cycles (PGCLK) */
#define TSC_CTPL_11CYCLES (TSC_CR_CTPL_3 | TSC_CR_CTPL_1) /*!< Charge transfer pulse low during 11 cycles (PGCLK) */
#define TSC_CTPL_12CYCLES (TSC_CR_CTPL_3 | TSC_CR_CTPL_1 | TSC_CR_CTPL_0) /*!< Charge transfer pulse low during 12 cycles (PGCLK) */
#define TSC_CTPL_13CYCLES (TSC_CR_CTPL_3 | TSC_CR_CTPL_2) /*!< Charge transfer pulse low during 13 cycles (PGCLK) */
#define TSC_CTPL_14CYCLES (TSC_CR_CTPL_3 | TSC_CR_CTPL_2 | TSC_CR_CTPL_0) /*!< Charge transfer pulse low during 14 cycles (PGCLK) */
#define TSC_CTPL_15CYCLES (TSC_CR_CTPL_3 | TSC_CR_CTPL_2 | TSC_CR_CTPL_1) /*!< Charge transfer pulse low during 15 cycles (PGCLK) */
#define TSC_CTPL_16CYCLES (TSC_CR_CTPL_3 | TSC_CR_CTPL_2 | TSC_CR_CTPL_1 | TSC_CR_CTPL_0) /*!< Charge transfer pulse low during 16 cycles (PGCLK) */
/**
* @}
*/
/** @defgroup TSC_SpreadSpec_Prescaler Spread Spectrum Prescaler
* @{
*/
#define TSC_SS_PRESC_DIV1 0x00000000UL /*!< Spread Spectrum Prescaler Div1 */
#define TSC_SS_PRESC_DIV2 TSC_CR_SSPSC /*!< Spread Spectrum Prescaler Div2 */
/**
* @}
*/
/** @defgroup TSC_PulseGenerator_Prescaler Pulse Generator Prescaler
* @{
*/
#define TSC_PG_PRESC_DIV1 0x00000000UL /*!< Pulse Generator HCLK Div1 */
#define TSC_PG_PRESC_DIV2 TSC_CR_PGPSC_0 /*!< Pulse Generator HCLK Div2 */
#define TSC_PG_PRESC_DIV4 TSC_CR_PGPSC_1 /*!< Pulse Generator HCLK Div4 */
#define TSC_PG_PRESC_DIV8 (TSC_CR_PGPSC_1 | TSC_CR_PGPSC_0) /*!< Pulse Generator HCLK Div8 */
#define TSC_PG_PRESC_DIV16 TSC_CR_PGPSC_2 /*!< Pulse Generator HCLK Div16 */
#define TSC_PG_PRESC_DIV32 (TSC_CR_PGPSC_2 | TSC_CR_PGPSC_0) /*!< Pulse Generator HCLK Div32 */
#define TSC_PG_PRESC_DIV64 (TSC_CR_PGPSC_2 | TSC_CR_PGPSC_1) /*!< Pulse Generator HCLK Div64 */
#define TSC_PG_PRESC_DIV128 (TSC_CR_PGPSC_2 | TSC_CR_PGPSC_1 | TSC_CR_PGPSC_0) /*!< Pulse Generator HCLK Div128 */
/**
* @}
*/
/** @defgroup TSC_MaxCount_Value Max Count Value
* @{
*/
#define TSC_MCV_255 0x00000000UL /*!< 255 maximum number of charge transfer pulses */
#define TSC_MCV_511 TSC_CR_MCV_0 /*!< 511 maximum number of charge transfer pulses */
#define TSC_MCV_1023 TSC_CR_MCV_1 /*!< 1023 maximum number of charge transfer pulses */
#define TSC_MCV_2047 (TSC_CR_MCV_1 | TSC_CR_MCV_0) /*!< 2047 maximum number of charge transfer pulses */
#define TSC_MCV_4095 TSC_CR_MCV_2 /*!< 4095 maximum number of charge transfer pulses */
#define TSC_MCV_8191 (TSC_CR_MCV_2 | TSC_CR_MCV_0) /*!< 8191 maximum number of charge transfer pulses */
#define TSC_MCV_16383 (TSC_CR_MCV_2 | TSC_CR_MCV_1) /*!< 16383 maximum number of charge transfer pulses */
/**
* @}
*/
/** @defgroup TSC_IO_Default_Mode IO Default Mode
* @{
*/
#define TSC_IODEF_OUT_PP_LOW 0x00000000UL /*!< I/Os are forced to output push-pull low */
#define TSC_IODEF_IN_FLOAT TSC_CR_IODEF /*!< I/Os are in input floating */
/**
* @}
*/
/** @defgroup TSC_Synchro_Pin_Polarity Synchro Pin Polarity
* @{
*/
#define TSC_SYNC_POLARITY_FALLING 0x00000000UL /*!< Falling edge only */
#define TSC_SYNC_POLARITY_RISING TSC_CR_SYNCPOL /*!< Rising edge and high level */
/**
* @}
*/
/** @defgroup TSC_Acquisition_Mode Acquisition Mode
* @{
*/
#define TSC_ACQ_MODE_NORMAL 0x00000000UL /*!< Normal acquisition mode (acquisition starts as soon as START bit is set) */
#define TSC_ACQ_MODE_SYNCHRO TSC_CR_AM /*!< Synchronized acquisition mode (acquisition starts if START bit is set and when the selected signal is detected on the SYNC input pin) */
/**
* @}
*/
/** @defgroup TSC_interrupts_definition Interrupts definition
* @{
*/
#define TSC_IT_EOA TSC_IER_EOAIE /*!< End of acquisition interrupt enable */
#define TSC_IT_MCE TSC_IER_MCEIE /*!< Max count error interrupt enable */
/**
* @}
*/
/** @defgroup TSC_flags_definition Flags definition
* @{
*/
#define TSC_FLAG_EOA TSC_ISR_EOAF /*!< End of acquisition flag */
#define TSC_FLAG_MCE TSC_ISR_MCEF /*!< Max count error flag */
/**
* @}
*/
/** @defgroup TSC_Group_definition Group definition
* @{
*/
#define TSC_GROUP1 (uint32_t)(0x1UL << TSC_GROUP1_IDX)
#define TSC_GROUP2 (uint32_t)(0x1UL << TSC_GROUP2_IDX)
#define TSC_GROUP3 (uint32_t)(0x1UL << TSC_GROUP3_IDX)
#define TSC_GROUP4 (uint32_t)(0x1UL << TSC_GROUP4_IDX)
#if defined(TSC_IOCCR_G5_IO1)
#define TSC_GROUP5 (uint32_t)(0x1UL << TSC_GROUP5_IDX)
#endif
#if defined(TSC_IOCCR_G6_IO1)
#define TSC_GROUP6 (uint32_t)(0x1UL << TSC_GROUP6_IDX)
#endif
#if defined(TSC_IOCCR_G7_IO1)
#define TSC_GROUP7 (uint32_t)(0x1UL << TSC_GROUP7_IDX)
#endif
#if defined(TSC_IOCCR_G8_IO1)
#define TSC_GROUP8 (uint32_t)(0x1UL << TSC_GROUP8_IDX)
#endif
#define TSC_GROUPX_NOT_SUPPORTED 0xFF000000UL /*!< TSC GroupX not supported */
#define TSC_GROUP1_IO1 TSC_IOCCR_G1_IO1 /*!< TSC Group1 IO1 */
#define TSC_GROUP1_IO2 TSC_IOCCR_G1_IO2 /*!< TSC Group1 IO2 */
#define TSC_GROUP1_IO3 TSC_IOCCR_G1_IO3 /*!< TSC Group1 IO3 */
#define TSC_GROUP1_IO4 TSC_IOCCR_G1_IO4 /*!< TSC Group1 IO4 */
#define TSC_GROUP2_IO1 TSC_IOCCR_G2_IO1 /*!< TSC Group2 IO1 */
#define TSC_GROUP2_IO2 TSC_IOCCR_G2_IO2 /*!< TSC Group2 IO2 */
#define TSC_GROUP2_IO3 TSC_IOCCR_G2_IO3 /*!< TSC Group2 IO3 */
#define TSC_GROUP2_IO4 TSC_IOCCR_G2_IO4 /*!< TSC Group2 IO4 */
#define TSC_GROUP3_IO1 TSC_IOCCR_G3_IO1 /*!< TSC Group3 IO1 */
#define TSC_GROUP3_IO2 TSC_IOCCR_G3_IO2 /*!< TSC Group3 IO2 */
#define TSC_GROUP3_IO3 TSC_IOCCR_G3_IO3 /*!< TSC Group3 IO3 */
#define TSC_GROUP3_IO4 TSC_IOCCR_G3_IO4 /*!< TSC Group3 IO4 */
#define TSC_GROUP4_IO1 TSC_IOCCR_G4_IO1 /*!< TSC Group4 IO1 */
#define TSC_GROUP4_IO2 TSC_IOCCR_G4_IO2 /*!< TSC Group4 IO2 */
#define TSC_GROUP4_IO3 TSC_IOCCR_G4_IO3 /*!< TSC Group4 IO3 */
#define TSC_GROUP4_IO4 TSC_IOCCR_G4_IO4 /*!< TSC Group4 IO4 */
#if defined(TSC_IOCCR_G5_IO1)
#define TSC_GROUP5_IO1 TSC_IOCCR_G5_IO1 /*!< TSC Group5 IO1 */
#define TSC_GROUP5_IO2 TSC_IOCCR_G5_IO2 /*!< TSC Group5 IO2 */
#define TSC_GROUP5_IO3 TSC_IOCCR_G5_IO3 /*!< TSC Group5 IO3 */
#define TSC_GROUP5_IO4 TSC_IOCCR_G5_IO4 /*!< TSC Group5 IO4 */
#else
#define TSC_GROUP5_IO1 (uint32_t)(0x00000010UL | TSC_GROUPX_NOT_SUPPORTED) /*!< TSC Group5 IO1 not supported */
#define TSC_GROUP5_IO2 TSC_GROUP5_IO1 /*!< TSC Group5 IO2 not supported */
#define TSC_GROUP5_IO3 TSC_GROUP5_IO1 /*!< TSC Group5 IO3 not supported */
#define TSC_GROUP5_IO4 TSC_GROUP5_IO1 /*!< TSC Group5 IO4 not supported */
#endif
#if defined(TSC_IOCCR_G6_IO1)
#define TSC_GROUP6_IO1 TSC_IOCCR_G6_IO1 /*!< TSC Group6 IO1 */
#define TSC_GROUP6_IO2 TSC_IOCCR_G6_IO2 /*!< TSC Group6 IO2 */
#define TSC_GROUP6_IO3 TSC_IOCCR_G6_IO3 /*!< TSC Group6 IO3 */
#define TSC_GROUP6_IO4 TSC_IOCCR_G6_IO4 /*!< TSC Group6 IO4 */
#else
#define TSC_GROUP6_IO1 (uint32_t)(0x00000020UL | TSC_GROUPX_NOT_SUPPORTED) /*!< TSC Group6 IO1 not supported */
#define TSC_GROUP6_IO2 TSC_GROUP6_IO1 /*!< TSC Group6 IO2 not supported */
#define TSC_GROUP6_IO3 TSC_GROUP6_IO1 /*!< TSC Group6 IO3 not supported */
#define TSC_GROUP6_IO4 TSC_GROUP6_IO1 /*!< TSC Group6 IO4 not supported */
#endif
#if defined(TSC_IOCCR_G7_IO1)
#define TSC_GROUP7_IO1 TSC_IOCCR_G7_IO1 /*!< TSC Group7 IO1 */
#define TSC_GROUP7_IO2 TSC_IOCCR_G7_IO2 /*!< TSC Group7 IO2 */
#define TSC_GROUP7_IO3 TSC_IOCCR_G7_IO3 /*!< TSC Group7 IO3 */
#define TSC_GROUP7_IO4 TSC_IOCCR_G7_IO4 /*!< TSC Group7 IO4 */
#else
#define TSC_GROUP7_IO1 (uint32_t)(0x00000040UL | TSC_GROUPX_NOT_SUPPORTED) /*!< TSC Group7 IO1 not supported */
#define TSC_GROUP7_IO2 TSC_GROUP7_IO1 /*!< TSC Group7 IO2 not supported */
#define TSC_GROUP7_IO3 TSC_GROUP7_IO1 /*!< TSC Group7 IO3 not supported */
#define TSC_GROUP7_IO4 TSC_GROUP7_IO1 /*!< TSC Group7 IO4 not supported */
#endif
#if defined(TSC_IOCCR_G8_IO1)
#define TSC_GROUP8_IO1 TSC_IOCCR_G8_IO1 /*!< TSC Group8 IO1 */
#define TSC_GROUP8_IO2 TSC_IOCCR_G8_IO2 /*!< TSC Group8 IO2 */
#define TSC_GROUP8_IO3 TSC_IOCCR_G8_IO3 /*!< TSC Group8 IO3 */
#define TSC_GROUP8_IO4 TSC_IOCCR_G8_IO4 /*!< TSC Group8 IO4 */
#else
#define TSC_GROUP8_IO1 (uint32_t)(0x00000080UL | TSC_GROUPX_NOT_SUPPORTED) /*!< TSC Group8 IO1 not supported */
#define TSC_GROUP8_IO2 TSC_GROUP8_IO1 /*!< TSC Group8 IO2 not supported */
#define TSC_GROUP8_IO3 TSC_GROUP8_IO1 /*!< TSC Group8 IO3 not supported */
#define TSC_GROUP8_IO4 TSC_GROUP8_IO1 /*!< TSC Group8 IO4 not supported */
#endif
/**
* @}
*/
/**
* @}
*/
/* Exported macros -----------------------------------------------------------*/
/** @defgroup TSC_Exported_Macros TSC Exported Macros
* @{
*/
/** @brief Reset TSC handle state.
* @param __HANDLE__ TSC handle
* @retval None
*/
#if (USE_HAL_TSC_REGISTER_CALLBACKS == 1)
#define __HAL_TSC_RESET_HANDLE_STATE(__HANDLE__) do{ \
(__HANDLE__)->State = HAL_TSC_STATE_RESET; \
(__HANDLE__)->MspInitCallback = NULL; \
(__HANDLE__)->MspDeInitCallback = NULL; \
} while(0)
#else
#define __HAL_TSC_RESET_HANDLE_STATE(__HANDLE__) ((__HANDLE__)->State = HAL_TSC_STATE_RESET)
#endif
/**
* @brief Enable the TSC peripheral.
* @param __HANDLE__ TSC handle
* @retval None
*/
#define __HAL_TSC_ENABLE(__HANDLE__) ((__HANDLE__)->Instance->CR |= TSC_CR_TSCE)
/**
* @brief Disable the TSC peripheral.
* @param __HANDLE__ TSC handle
* @retval None
*/
#define __HAL_TSC_DISABLE(__HANDLE__) ((__HANDLE__)->Instance->CR &= (uint32_t)(~TSC_CR_TSCE))
/**
* @brief Start acquisition.
* @param __HANDLE__ TSC handle
* @retval None
*/
#define __HAL_TSC_START_ACQ(__HANDLE__) ((__HANDLE__)->Instance->CR |= TSC_CR_START)
/**
* @brief Stop acquisition.
* @param __HANDLE__ TSC handle
* @retval None
*/
#define __HAL_TSC_STOP_ACQ(__HANDLE__) ((__HANDLE__)->Instance->CR &= (uint32_t)(~TSC_CR_START))
/**
* @brief Set IO default mode to output push-pull low.
* @param __HANDLE__ TSC handle
* @retval None
*/
#define __HAL_TSC_SET_IODEF_OUTPPLOW(__HANDLE__) ((__HANDLE__)->Instance->CR &= (uint32_t)(~TSC_CR_IODEF))
/**
* @brief Set IO default mode to input floating.
* @param __HANDLE__ TSC handle
* @retval None
*/
#define __HAL_TSC_SET_IODEF_INFLOAT(__HANDLE__) ((__HANDLE__)->Instance->CR |= TSC_CR_IODEF)
/**
* @brief Set synchronization polarity to falling edge.
* @param __HANDLE__ TSC handle
* @retval None
*/
#define __HAL_TSC_SET_SYNC_POL_FALL(__HANDLE__) ((__HANDLE__)->Instance->CR &= (uint32_t)(~TSC_CR_SYNCPOL))
/**
* @brief Set synchronization polarity to rising edge and high level.
* @param __HANDLE__ TSC handle
* @retval None
*/
#define __HAL_TSC_SET_SYNC_POL_RISE_HIGH(__HANDLE__) ((__HANDLE__)->Instance->CR |= TSC_CR_SYNCPOL)
/**
* @brief Enable TSC interrupt.
* @param __HANDLE__ TSC handle
* @param __INTERRUPT__ TSC interrupt
* @retval None
*/
#define __HAL_TSC_ENABLE_IT(__HANDLE__, __INTERRUPT__) ((__HANDLE__)->Instance->IER |= (__INTERRUPT__))
/**
* @brief Disable TSC interrupt.
* @param __HANDLE__ TSC handle
* @param __INTERRUPT__ TSC interrupt
* @retval None
*/
#define __HAL_TSC_DISABLE_IT(__HANDLE__, __INTERRUPT__) ((__HANDLE__)->Instance->IER &= (uint32_t)(~(__INTERRUPT__)))
/** @brief Check whether the specified TSC interrupt source is enabled or not.
* @param __HANDLE__ TSC Handle
* @param __INTERRUPT__ TSC interrupt
* @retval SET or RESET
*/
#define __HAL_TSC_GET_IT_SOURCE(__HANDLE__, __INTERRUPT__) ((((__HANDLE__)->Instance->IER & (__INTERRUPT__)) == (__INTERRUPT__)) ? SET : RESET)
/**
* @brief Check whether the specified TSC flag is set or not.
* @param __HANDLE__ TSC handle
* @param __FLAG__ TSC flag
* @retval SET or RESET
*/
#define __HAL_TSC_GET_FLAG(__HANDLE__, __FLAG__) ((((__HANDLE__)->Instance->ISR & (__FLAG__)) == (__FLAG__)) ? SET : RESET)
/**
* @brief Clear the TSC's pending flag.
* @param __HANDLE__ TSC handle
* @param __FLAG__ TSC flag
* @retval None
*/
#define __HAL_TSC_CLEAR_FLAG(__HANDLE__, __FLAG__) ((__HANDLE__)->Instance->ICR = (__FLAG__))
/**
* @brief Enable schmitt trigger hysteresis on a group of IOs.
* @param __HANDLE__ TSC handle
* @param __GX_IOY_MASK__ IOs mask
* @retval None
*/
#define __HAL_TSC_ENABLE_HYSTERESIS(__HANDLE__, __GX_IOY_MASK__) ((__HANDLE__)->Instance->IOHCR |= (__GX_IOY_MASK__))
/**
* @brief Disable schmitt trigger hysteresis on a group of IOs.
* @param __HANDLE__ TSC handle
* @param __GX_IOY_MASK__ IOs mask
* @retval None
*/
#define __HAL_TSC_DISABLE_HYSTERESIS(__HANDLE__, __GX_IOY_MASK__) ((__HANDLE__)->Instance->IOHCR &= (uint32_t)(~(__GX_IOY_MASK__)))
/**
* @brief Open analog switch on a group of IOs.
* @param __HANDLE__ TSC handle
* @param __GX_IOY_MASK__ IOs mask
* @retval None
*/
#define __HAL_TSC_OPEN_ANALOG_SWITCH(__HANDLE__, __GX_IOY_MASK__) ((__HANDLE__)->Instance->IOASCR &= (uint32_t)(~(__GX_IOY_MASK__)))
/**
* @brief Close analog switch on a group of IOs.
* @param __HANDLE__ TSC handle
* @param __GX_IOY_MASK__ IOs mask
* @retval None
*/
#define __HAL_TSC_CLOSE_ANALOG_SWITCH(__HANDLE__, __GX_IOY_MASK__) ((__HANDLE__)->Instance->IOASCR |= (__GX_IOY_MASK__))
/**
* @brief Enable a group of IOs in channel mode.
* @param __HANDLE__ TSC handle
* @param __GX_IOY_MASK__ IOs mask
* @retval None
*/
#define __HAL_TSC_ENABLE_CHANNEL(__HANDLE__, __GX_IOY_MASK__) ((__HANDLE__)->Instance->IOCCR |= (__GX_IOY_MASK__))
/**
* @brief Disable a group of channel IOs.
* @param __HANDLE__ TSC handle
* @param __GX_IOY_MASK__ IOs mask
* @retval None
*/
#define __HAL_TSC_DISABLE_CHANNEL(__HANDLE__, __GX_IOY_MASK__) ((__HANDLE__)->Instance->IOCCR &= (uint32_t)(~(__GX_IOY_MASK__)))
/**
* @brief Enable a group of IOs in sampling mode.
* @param __HANDLE__ TSC handle
* @param __GX_IOY_MASK__ IOs mask
* @retval None
*/
#define __HAL_TSC_ENABLE_SAMPLING(__HANDLE__, __GX_IOY_MASK__) ((__HANDLE__)->Instance->IOSCR |= (__GX_IOY_MASK__))
/**
* @brief Disable a group of sampling IOs.
* @param __HANDLE__ TSC handle
* @param __GX_IOY_MASK__ IOs mask
* @retval None
*/
#define __HAL_TSC_DISABLE_SAMPLING(__HANDLE__, __GX_IOY_MASK__) ((__HANDLE__)->Instance->IOSCR &= (uint32_t)(~(__GX_IOY_MASK__)))
/**
* @brief Enable acquisition groups.
* @param __HANDLE__ TSC handle
* @param __GX_MASK__ Groups mask
* @retval None
*/
#define __HAL_TSC_ENABLE_GROUP(__HANDLE__, __GX_MASK__) ((__HANDLE__)->Instance->IOGCSR |= (__GX_MASK__))
/**
* @brief Disable acquisition groups.
* @param __HANDLE__ TSC handle
* @param __GX_MASK__ Groups mask
* @retval None
*/
#define __HAL_TSC_DISABLE_GROUP(__HANDLE__, __GX_MASK__) ((__HANDLE__)->Instance->IOGCSR &= (uint32_t)(~(__GX_MASK__)))
/** @brief Gets acquisition group status.
* @param __HANDLE__ TSC Handle
* @param __GX_INDEX__ Group index
* @retval SET or RESET
*/
#define __HAL_TSC_GET_GROUP_STATUS(__HANDLE__, __GX_INDEX__) \
((((__HANDLE__)->Instance->IOGCSR & (uint32_t)(1UL << (((__GX_INDEX__) & (uint32_t)TSC_NB_OF_GROUPS) + 16UL))) == (uint32_t)(1UL << (((__GX_INDEX__) & (uint32_t)TSC_NB_OF_GROUPS) + 16UL))) ? TSC_GROUP_COMPLETED : TSC_GROUP_ONGOING)
/**
* @}
*/
/* Private macros ------------------------------------------------------------*/
/** @defgroup TSC_Private_Macros TSC Private Macros
* @{
*/
#define IS_TSC_CTPH(__VALUE__) (((__VALUE__) == TSC_CTPH_1CYCLE) || \
((__VALUE__) == TSC_CTPH_2CYCLES) || \
((__VALUE__) == TSC_CTPH_3CYCLES) || \
((__VALUE__) == TSC_CTPH_4CYCLES) || \
((__VALUE__) == TSC_CTPH_5CYCLES) || \
((__VALUE__) == TSC_CTPH_6CYCLES) || \
((__VALUE__) == TSC_CTPH_7CYCLES) || \
((__VALUE__) == TSC_CTPH_8CYCLES) || \
((__VALUE__) == TSC_CTPH_9CYCLES) || \
((__VALUE__) == TSC_CTPH_10CYCLES) || \
((__VALUE__) == TSC_CTPH_11CYCLES) || \
((__VALUE__) == TSC_CTPH_12CYCLES) || \
((__VALUE__) == TSC_CTPH_13CYCLES) || \
((__VALUE__) == TSC_CTPH_14CYCLES) || \
((__VALUE__) == TSC_CTPH_15CYCLES) || \
((__VALUE__) == TSC_CTPH_16CYCLES))
#define IS_TSC_CTPL(__VALUE__) (((__VALUE__) == TSC_CTPL_1CYCLE) || \
((__VALUE__) == TSC_CTPL_2CYCLES) || \
((__VALUE__) == TSC_CTPL_3CYCLES) || \
((__VALUE__) == TSC_CTPL_4CYCLES) || \
((__VALUE__) == TSC_CTPL_5CYCLES) || \
((__VALUE__) == TSC_CTPL_6CYCLES) || \
((__VALUE__) == TSC_CTPL_7CYCLES) || \
((__VALUE__) == TSC_CTPL_8CYCLES) || \
((__VALUE__) == TSC_CTPL_9CYCLES) || \
((__VALUE__) == TSC_CTPL_10CYCLES) || \
((__VALUE__) == TSC_CTPL_11CYCLES) || \
((__VALUE__) == TSC_CTPL_12CYCLES) || \
((__VALUE__) == TSC_CTPL_13CYCLES) || \
((__VALUE__) == TSC_CTPL_14CYCLES) || \
((__VALUE__) == TSC_CTPL_15CYCLES) || \
((__VALUE__) == TSC_CTPL_16CYCLES))
#define IS_TSC_SS(__VALUE__) (((FunctionalState)(__VALUE__) == DISABLE) || ((FunctionalState)(__VALUE__) == ENABLE))
#define IS_TSC_SSD(__VALUE__) (((__VALUE__) == 0UL) || (((__VALUE__) > 0UL) && ((__VALUE__) < 128UL)))
#define IS_TSC_SS_PRESC(__VALUE__) (((__VALUE__) == TSC_SS_PRESC_DIV1) || ((__VALUE__) == TSC_SS_PRESC_DIV2))
#define IS_TSC_PG_PRESC(__VALUE__) (((__VALUE__) == TSC_PG_PRESC_DIV1) || \
((__VALUE__) == TSC_PG_PRESC_DIV2) || \
((__VALUE__) == TSC_PG_PRESC_DIV4) || \
((__VALUE__) == TSC_PG_PRESC_DIV8) || \
((__VALUE__) == TSC_PG_PRESC_DIV16) || \
((__VALUE__) == TSC_PG_PRESC_DIV32) || \
((__VALUE__) == TSC_PG_PRESC_DIV64) || \
((__VALUE__) == TSC_PG_PRESC_DIV128))
#define IS_TSC_MCV(__VALUE__) (((__VALUE__) == TSC_MCV_255) || \
((__VALUE__) == TSC_MCV_511) || \
((__VALUE__) == TSC_MCV_1023) || \
((__VALUE__) == TSC_MCV_2047) || \
((__VALUE__) == TSC_MCV_4095) || \
((__VALUE__) == TSC_MCV_8191) || \
((__VALUE__) == TSC_MCV_16383))
#define IS_TSC_IODEF(__VALUE__) (((__VALUE__) == TSC_IODEF_OUT_PP_LOW) || ((__VALUE__) == TSC_IODEF_IN_FLOAT))
#define IS_TSC_SYNC_POL(__VALUE__) (((__VALUE__) == TSC_SYNC_POLARITY_FALLING) || ((__VALUE__) == TSC_SYNC_POLARITY_RISING))
#define IS_TSC_ACQ_MODE(__VALUE__) (((__VALUE__) == TSC_ACQ_MODE_NORMAL) || ((__VALUE__) == TSC_ACQ_MODE_SYNCHRO))
#define IS_TSC_MCE_IT(__VALUE__) (((FunctionalState)(__VALUE__) == DISABLE) || ((FunctionalState)(__VALUE__) == ENABLE))
#define IS_TSC_GROUP_INDEX(__VALUE__) (((__VALUE__) == 0UL) || (((__VALUE__) > 0UL) && ((__VALUE__) < (uint32_t)TSC_NB_OF_GROUPS)))
#define IS_TSC_GROUP(__VALUE__) ((((__VALUE__) & TSC_GROUPX_NOT_SUPPORTED) != TSC_GROUPX_NOT_SUPPORTED) && \
((((__VALUE__) & TSC_GROUP1_IO1) == TSC_GROUP1_IO1) ||\
(((__VALUE__) & TSC_GROUP1_IO2) == TSC_GROUP1_IO2) ||\
(((__VALUE__) & TSC_GROUP1_IO3) == TSC_GROUP1_IO3) ||\
(((__VALUE__) & TSC_GROUP1_IO4) == TSC_GROUP1_IO4) ||\
(((__VALUE__) & TSC_GROUP2_IO1) == TSC_GROUP2_IO1) ||\
(((__VALUE__) & TSC_GROUP2_IO2) == TSC_GROUP2_IO2) ||\
(((__VALUE__) & TSC_GROUP2_IO3) == TSC_GROUP2_IO3) ||\
(((__VALUE__) & TSC_GROUP2_IO4) == TSC_GROUP2_IO4) ||\
(((__VALUE__) & TSC_GROUP3_IO1) == TSC_GROUP3_IO1) ||\
(((__VALUE__) & TSC_GROUP3_IO2) == TSC_GROUP3_IO2) ||\
(((__VALUE__) & TSC_GROUP3_IO3) == TSC_GROUP3_IO3) ||\
(((__VALUE__) & TSC_GROUP3_IO4) == TSC_GROUP3_IO4) ||\
(((__VALUE__) & TSC_GROUP4_IO1) == TSC_GROUP4_IO1) ||\
(((__VALUE__) & TSC_GROUP4_IO2) == TSC_GROUP4_IO2) ||\
(((__VALUE__) & TSC_GROUP4_IO3) == TSC_GROUP4_IO3) ||\
(((__VALUE__) & TSC_GROUP4_IO4) == TSC_GROUP4_IO4) ||\
(((__VALUE__) & TSC_GROUP5_IO1) == TSC_GROUP5_IO1) ||\
(((__VALUE__) & TSC_GROUP5_IO2) == TSC_GROUP5_IO2) ||\
(((__VALUE__) & TSC_GROUP5_IO3) == TSC_GROUP5_IO3) ||\
(((__VALUE__) & TSC_GROUP5_IO4) == TSC_GROUP5_IO4) ||\
(((__VALUE__) & TSC_GROUP6_IO1) == TSC_GROUP6_IO1) ||\
(((__VALUE__) & TSC_GROUP6_IO2) == TSC_GROUP6_IO2) ||\
(((__VALUE__) & TSC_GROUP6_IO3) == TSC_GROUP6_IO3) ||\
(((__VALUE__) & TSC_GROUP6_IO4) == TSC_GROUP6_IO4) ||\
(((__VALUE__) & TSC_GROUP7_IO1) == TSC_GROUP7_IO1) ||\
(((__VALUE__) & TSC_GROUP7_IO2) == TSC_GROUP7_IO2) ||\
(((__VALUE__) & TSC_GROUP7_IO3) == TSC_GROUP7_IO3) ||\
(((__VALUE__) & TSC_GROUP7_IO4) == TSC_GROUP7_IO4) ||\
(((__VALUE__) & TSC_GROUP8_IO1) == TSC_GROUP8_IO1) ||\
(((__VALUE__) & TSC_GROUP8_IO2) == TSC_GROUP8_IO2) ||\
(((__VALUE__) & TSC_GROUP8_IO3) == TSC_GROUP8_IO3) ||\
(((__VALUE__) & TSC_GROUP8_IO4) == TSC_GROUP8_IO4)))
/**
* @}
*/
/* Exported functions --------------------------------------------------------*/
/** @addtogroup TSC_Exported_Functions
* @{
*/
/** @addtogroup TSC_Exported_Functions_Group1 Initialization and de-initialization functions
* @{
*/
/* Initialization and de-initialization functions *****************************/
HAL_StatusTypeDef HAL_TSC_Init(TSC_HandleTypeDef *htsc);
HAL_StatusTypeDef HAL_TSC_DeInit(TSC_HandleTypeDef *htsc);
void HAL_TSC_MspInit(TSC_HandleTypeDef *htsc);
void HAL_TSC_MspDeInit(TSC_HandleTypeDef *htsc);
/* Callbacks Register/UnRegister functions ***********************************/
#if (USE_HAL_TSC_REGISTER_CALLBACKS == 1)
HAL_StatusTypeDef HAL_TSC_RegisterCallback(TSC_HandleTypeDef *htsc, HAL_TSC_CallbackIDTypeDef CallbackID, pTSC_CallbackTypeDef pCallback);
HAL_StatusTypeDef HAL_TSC_UnRegisterCallback(TSC_HandleTypeDef *htsc, HAL_TSC_CallbackIDTypeDef CallbackID);
#endif /* USE_HAL_TSC_REGISTER_CALLBACKS */
/**
* @}
*/
/** @addtogroup TSC_Exported_Functions_Group2 Input and Output operation functions
* @{
*/
/* IO operation functions *****************************************************/
HAL_StatusTypeDef HAL_TSC_Start(TSC_HandleTypeDef *htsc);
HAL_StatusTypeDef HAL_TSC_Start_IT(TSC_HandleTypeDef *htsc);
HAL_StatusTypeDef HAL_TSC_Stop(TSC_HandleTypeDef *htsc);
HAL_StatusTypeDef HAL_TSC_Stop_IT(TSC_HandleTypeDef *htsc);
HAL_StatusTypeDef HAL_TSC_PollForAcquisition(TSC_HandleTypeDef *htsc);
TSC_GroupStatusTypeDef HAL_TSC_GroupGetStatus(TSC_HandleTypeDef *htsc, uint32_t gx_index);
uint32_t HAL_TSC_GroupGetValue(TSC_HandleTypeDef *htsc, uint32_t gx_index);
/**
* @}
*/
/** @addtogroup TSC_Exported_Functions_Group3 Peripheral Control functions
* @{
*/
/* Peripheral Control functions ***********************************************/
HAL_StatusTypeDef HAL_TSC_IOConfig(TSC_HandleTypeDef *htsc, TSC_IOConfigTypeDef *config);
HAL_StatusTypeDef HAL_TSC_IODischarge(TSC_HandleTypeDef *htsc, uint32_t choice);
/**
* @}
*/
/** @addtogroup TSC_Exported_Functions_Group4 Peripheral State and Errors functions
* @{
*/
/* Peripheral State and Error functions ***************************************/
HAL_TSC_StateTypeDef HAL_TSC_GetState(TSC_HandleTypeDef *htsc);
/**
* @}
*/
/** @addtogroup TSC_IRQ_Handler_and_Callbacks IRQ Handler and Callbacks
* @{
*/
/******* TSC IRQHandler and Callbacks used in Interrupt mode */
void HAL_TSC_IRQHandler(TSC_HandleTypeDef *htsc);
void HAL_TSC_ConvCpltCallback(TSC_HandleTypeDef *htsc);
void HAL_TSC_ErrorCallback(TSC_HandleTypeDef *htsc);
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* STM32L4xx_HAL_TSC_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@ -0,0 +1,290 @@
/**
******************************************************************************
* @file stm32l4xx_ll_exti.c
* @author MCD Application Team
* @brief EXTI LL module driver.
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2017 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
#if defined(USE_FULL_LL_DRIVER)
/* Includes ------------------------------------------------------------------*/
#include "stm32l4xx_ll_exti.h"
#ifdef USE_FULL_ASSERT
#include "stm32_assert.h"
#else
#define assert_param(expr) ((void)0U)
#endif
/** @addtogroup STM32L4xx_LL_Driver
* @{
*/
#if defined (EXTI)
/** @defgroup EXTI_LL EXTI
* @{
*/
/* Private types -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private constants ---------------------------------------------------------*/
/* Private macros ------------------------------------------------------------*/
/** @addtogroup EXTI_LL_Private_Macros
* @{
*/
#define IS_LL_EXTI_LINE_0_31(__VALUE__) (((__VALUE__) & ~LL_EXTI_LINE_ALL_0_31) == 0x00000000U)
#define IS_LL_EXTI_LINE_32_63(__VALUE__) (((__VALUE__) & ~LL_EXTI_LINE_ALL_32_63) == 0x00000000U)
#define IS_LL_EXTI_MODE(__VALUE__) (((__VALUE__) == LL_EXTI_MODE_IT) \
|| ((__VALUE__) == LL_EXTI_MODE_EVENT) \
|| ((__VALUE__) == LL_EXTI_MODE_IT_EVENT))
#define IS_LL_EXTI_TRIGGER(__VALUE__) (((__VALUE__) == LL_EXTI_TRIGGER_NONE) \
|| ((__VALUE__) == LL_EXTI_TRIGGER_RISING) \
|| ((__VALUE__) == LL_EXTI_TRIGGER_FALLING) \
|| ((__VALUE__) == LL_EXTI_TRIGGER_RISING_FALLING))
/**
* @}
*/
/* Private function prototypes -----------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/** @addtogroup EXTI_LL_Exported_Functions
* @{
*/
/** @addtogroup EXTI_LL_EF_Init
* @{
*/
/**
* @brief De-initialize the EXTI registers to their default reset values.
* @retval An ErrorStatus enumeration value:
* - 0x00: EXTI registers are de-initialized
*/
uint32_t LL_EXTI_DeInit(void)
{
/* Interrupt mask register set to default reset values */
LL_EXTI_WriteReg(IMR1, 0xFF820000U);
/* Event mask register set to default reset values */
LL_EXTI_WriteReg(EMR1, 0x00000000U);
/* Rising Trigger selection register set to default reset values */
LL_EXTI_WriteReg(RTSR1, 0x00000000U);
/* Falling Trigger selection register set to default reset values */
LL_EXTI_WriteReg(FTSR1, 0x00000000U);
/* Software interrupt event register set to default reset values */
LL_EXTI_WriteReg(SWIER1, 0x00000000U);
/* Pending register clear */
LL_EXTI_WriteReg(PR1, 0x007DFFFFU);
/* Interrupt mask register 2 set to default reset values */
#if defined(LL_EXTI_LINE_40)
LL_EXTI_WriteReg(IMR2, 0x00000187U);
#else
LL_EXTI_WriteReg(IMR2, 0x00000087U);
#endif
/* Event mask register 2 set to default reset values */
LL_EXTI_WriteReg(EMR2, 0x00000000U);
/* Rising Trigger selection register 2 set to default reset values */
LL_EXTI_WriteReg(RTSR2, 0x00000000U);
/* Falling Trigger selection register 2 set to default reset values */
LL_EXTI_WriteReg(FTSR2, 0x00000000U);
/* Software interrupt event register 2 set to default reset values */
LL_EXTI_WriteReg(SWIER2, 0x00000000U);
/* Pending register 2 clear */
LL_EXTI_WriteReg(PR2, 0x00000078U);
return 0x00u;
}
/**
* @brief Initialize the EXTI registers according to the specified parameters in EXTI_InitStruct.
* @param EXTI_InitStruct pointer to a @ref LL_EXTI_InitTypeDef structure.
* @retval An ErrorStatus enumeration value:
* - 0x00: EXTI registers are initialized
* - any other calue : wrong configuration
*/
uint32_t LL_EXTI_Init(LL_EXTI_InitTypeDef *EXTI_InitStruct)
{
uint32_t status = 0x00u;
/* Check the parameters */
assert_param(IS_LL_EXTI_LINE_0_31(EXTI_InitStruct->Line_0_31));
assert_param(IS_LL_EXTI_LINE_32_63(EXTI_InitStruct->Line_32_63));
assert_param(IS_FUNCTIONAL_STATE(EXTI_InitStruct->LineCommand));
assert_param(IS_LL_EXTI_MODE(EXTI_InitStruct->Mode));
/* ENABLE LineCommand */
if (EXTI_InitStruct->LineCommand != DISABLE)
{
assert_param(IS_LL_EXTI_TRIGGER(EXTI_InitStruct->Trigger));
/* Configure EXTI Lines in range from 0 to 31 */
if (EXTI_InitStruct->Line_0_31 != LL_EXTI_LINE_NONE)
{
switch (EXTI_InitStruct->Mode)
{
case LL_EXTI_MODE_IT:
/* First Disable Event on provided Lines */
LL_EXTI_DisableEvent_0_31(EXTI_InitStruct->Line_0_31);
/* Then Enable IT on provided Lines */
LL_EXTI_EnableIT_0_31(EXTI_InitStruct->Line_0_31);
break;
case LL_EXTI_MODE_EVENT:
/* First Disable IT on provided Lines */
LL_EXTI_DisableIT_0_31(EXTI_InitStruct->Line_0_31);
/* Then Enable Event on provided Lines */
LL_EXTI_EnableEvent_0_31(EXTI_InitStruct->Line_0_31);
break;
case LL_EXTI_MODE_IT_EVENT:
/* Directly Enable IT & Event on provided Lines */
LL_EXTI_EnableIT_0_31(EXTI_InitStruct->Line_0_31);
LL_EXTI_EnableEvent_0_31(EXTI_InitStruct->Line_0_31);
break;
default:
status = 0x01u;
break;
}
if (EXTI_InitStruct->Trigger != LL_EXTI_TRIGGER_NONE)
{
switch (EXTI_InitStruct->Trigger)
{
case LL_EXTI_TRIGGER_RISING:
/* First Disable Falling Trigger on provided Lines */
LL_EXTI_DisableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
/* Then Enable Rising Trigger on provided Lines */
LL_EXTI_EnableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
break;
case LL_EXTI_TRIGGER_FALLING:
/* First Disable Rising Trigger on provided Lines */
LL_EXTI_DisableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
/* Then Enable Falling Trigger on provided Lines */
LL_EXTI_EnableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
break;
case LL_EXTI_TRIGGER_RISING_FALLING:
LL_EXTI_EnableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
LL_EXTI_EnableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
break;
default:
status |= 0x02u;
break;
}
}
}
/* Configure EXTI Lines in range from 32 to 63 */
if (EXTI_InitStruct->Line_32_63 != LL_EXTI_LINE_NONE)
{
switch (EXTI_InitStruct->Mode)
{
case LL_EXTI_MODE_IT:
/* First Disable Event on provided Lines */
LL_EXTI_DisableEvent_32_63(EXTI_InitStruct->Line_32_63);
/* Then Enable IT on provided Lines */
LL_EXTI_EnableIT_32_63(EXTI_InitStruct->Line_32_63);
break;
case LL_EXTI_MODE_EVENT:
/* First Disable IT on provided Lines */
LL_EXTI_DisableIT_32_63(EXTI_InitStruct->Line_32_63);
/* Then Enable Event on provided Lines */
LL_EXTI_EnableEvent_32_63(EXTI_InitStruct->Line_32_63);
break;
case LL_EXTI_MODE_IT_EVENT:
/* Directly Enable IT & Event on provided Lines */
LL_EXTI_EnableIT_32_63(EXTI_InitStruct->Line_32_63);
LL_EXTI_EnableEvent_32_63(EXTI_InitStruct->Line_32_63);
break;
default:
status |= 0x04u;
break;
}
if (EXTI_InitStruct->Trigger != LL_EXTI_TRIGGER_NONE)
{
switch (EXTI_InitStruct->Trigger)
{
case LL_EXTI_TRIGGER_RISING:
/* First Disable Falling Trigger on provided Lines */
LL_EXTI_DisableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
/* Then Enable IT on provided Lines */
LL_EXTI_EnableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
break;
case LL_EXTI_TRIGGER_FALLING:
/* First Disable Rising Trigger on provided Lines */
LL_EXTI_DisableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
/* Then Enable Falling Trigger on provided Lines */
LL_EXTI_EnableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
break;
case LL_EXTI_TRIGGER_RISING_FALLING:
LL_EXTI_EnableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
LL_EXTI_EnableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
break;
default:
status = ERROR;
break;
}
}
}
}
/* DISABLE LineCommand */
else
{
/* De-configure EXTI Lines in range from 0 to 31 */
LL_EXTI_DisableIT_0_31(EXTI_InitStruct->Line_0_31);
LL_EXTI_DisableEvent_0_31(EXTI_InitStruct->Line_0_31);
/* De-configure EXTI Lines in range from 32 to 63 */
LL_EXTI_DisableIT_32_63(EXTI_InitStruct->Line_32_63);
LL_EXTI_DisableEvent_32_63(EXTI_InitStruct->Line_32_63);
}
return status;
}
/**
* @brief Set each @ref LL_EXTI_InitTypeDef field to default value.
* @param EXTI_InitStruct Pointer to a @ref LL_EXTI_InitTypeDef structure.
* @retval None
*/
void LL_EXTI_StructInit(LL_EXTI_InitTypeDef *EXTI_InitStruct)
{
EXTI_InitStruct->Line_0_31 = LL_EXTI_LINE_NONE;
EXTI_InitStruct->Line_32_63 = LL_EXTI_LINE_NONE;
EXTI_InitStruct->LineCommand = DISABLE;
EXTI_InitStruct->Mode = LL_EXTI_MODE_IT;
EXTI_InitStruct->Trigger = LL_EXTI_TRIGGER_FALLING;
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#endif /* defined (EXTI) */
/**
* @}
*/
#endif /* USE_FULL_LL_DRIVER */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

File diff suppressed because it is too large Load Diff

View File

@ -26,16 +26,18 @@ static uint8_t *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length);
static uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length);
#define NUM_INTERFACES 2
#define NUM_CLASSES 2
#define NUM_INTERFACES 3
#if NUM_INTERFACES>1
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (90)
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (90 + 8+9 + 4)
#else
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (41)
#endif
#define HID_INTF_NUM 0
#define CDC_INTF_NUM 1
#define CDC_MASTER_INTF_NUM 1
#define CDC_SLAVE_INTF_NUM 2
__ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_SIZE] __ALIGN_END =
{
/*Configuration Descriptor*/
@ -43,7 +45,7 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
COMPOSITE_CDC_HID_DESCRIPTOR_SIZE, /* wTotalLength:no of returned bytes */
0x00,
NUM_INTERFACES, /* bNumInterfaces: 1 interface */
NUM_INTERFACES, /* bNumInterfaces */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0x80, /* bmAttributes: self powered */
@ -57,7 +59,7 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
/************** Descriptor of Joystick Mouse interface ****************/
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
USB_DESC_TYPE_INTERFACE, /*bDescriptorType: Interface descriptor type*/
HID_INTF_NUM, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x02, /*bNumEndpoints*/
@ -73,7 +75,7 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
0x22, /*bDescriptorType*/
HID_FIDO_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
HID_FIDO_REPORT_DESC_SIZE, /*wItemLength: Total length of Report descriptor*/
0,
/******************** Descriptor of Mouse endpoint ********************/
0x07, /*bLength: Endpoint Descriptor size*/
@ -92,22 +94,28 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
0x00,
HID_BINTERVAL, /*bInterval: Polling Interval */
#if NUM_INTERFACES>1
#if NUM_INTERFACES > 1
/* */
/* CDC */
/* */
// This "IAD" is needed for Windows since it ignores the standard Union Functional Descriptor
0x08, // bLength
0x0B, // IAD type
CDC_MASTER_INTF_NUM, // First interface
CDC_SLAVE_INTF_NUM, // Next interface
0x02, // bInterfaceClass of the first interface
0x02, // bInterfaceSubClass of the first interface
0x00, // bInterfaceProtocol of the first interface
0x00, // Interface string index
/*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
/* Interface descriptor type */
/*!*/ CDC_INTF_NUM, /* bInterfaceNumber: Number of Interface */
/*!*/ CDC_MASTER_INTF_NUM, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x03, /* bNumEndpoints: 3 endpoints used */
0x01, /* bNumEndpoints: 1 endpoint used */
0x02, /* bInterfaceClass: Communication Interface Class */
0x02, /* bInterfaceSubClass: Abstract Control Model */
0x00, /* bInterfaceProtocol: Common AT commands */
@ -125,7 +133,7 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
0x24, /* bDescriptorType: CS_INTERFACE */
0x01, /* bDescriptorSubtype: Call Management Func Desc */
0x00, /* bmCapabilities: D0+D1 */
/*!*/ CDC_INTF_NUM, /* bDataInterface: 0 */
/*!*/ CDC_SLAVE_INTF_NUM, /* bDataInterface: 0 */
/*ACM Functional Descriptor*/
0x04, /* bFunctionLength */
@ -137,10 +145,10 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x06, /* bDescriptorSubtype: Union func desc */
/*!*/ CDC_INTF_NUM, /* bMasterInterface: Communication class interface */
/*!*/ CDC_INTF_NUM, /* bSlaveInterface0: Data Class Interface */
/*!*/ CDC_MASTER_INTF_NUM, /* bMasterInterface: Communication class interface */
/*!*/ CDC_SLAVE_INTF_NUM, /* bSlaveInterface0: Data Class Interface */
/*Endpoint 2 Descriptor*/
/* Control Endpoint Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_CMD_EP, /* bEndpointAddress */
@ -149,6 +157,17 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
HIBYTE(CDC_CMD_PACKET_SIZE),
0x10, /* bInterval: */
/* Interface descriptor */
0x09, /* bLength */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
CDC_SLAVE_INTF_NUM, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x02, /* bNumEndpoints */
0x0A, /* bInterfaceClass: Communication class data */
0x00, /* bInterfaceSubClass */
0x00, /* bInterfaceProtocol */
0x00,
/*Endpoint OUT Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
@ -167,10 +186,13 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x00, /* bInterval: ignore for Bulk transfer */
4, /* Descriptor size */
3, /* Descriptor type */
0x09,
0x04,
#endif
};
USBD_ClassTypeDef USBD_Composite =
{
USBD_Composite_Init,
@ -195,14 +217,27 @@ int in_endpoint_to_class[MAX_ENDPOINTS];
int out_endpoint_to_class[MAX_ENDPOINTS];
void USBD_Composite_Set_Classes(USBD_ClassTypeDef *class0, USBD_ClassTypeDef *class1) {
USBD_Classes[0] = class0;
USBD_Classes[1] = class1;
void USBD_Composite_Set_Classes(USBD_ClassTypeDef *hid_class, USBD_ClassTypeDef *cdc_class) {
USBD_Classes[0] = hid_class;
USBD_Classes[1] = cdc_class;
}
static USBD_ClassTypeDef * getClass(uint8_t index)
{
switch(index)
{
case HID_INTF_NUM:
return USBD_Classes[0];
case CDC_MASTER_INTF_NUM:
case CDC_SLAVE_INTF_NUM:
return USBD_Classes[1];
}
return NULL;
}
static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
int i;
for(i = 0; i < NUM_INTERFACES; i++) {
for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i]->Init(pdev, cfgidx) != USBD_OK) {
return USBD_FAIL;
}
@ -213,7 +248,7 @@ static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
int i;
for(i = 0; i < NUM_INTERFACES; i++) {
for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i]->DeInit(pdev, cfgidx) != USBD_OK) {
return USBD_FAIL;
}
@ -224,10 +259,13 @@ static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) {
int i;
USBD_ClassTypeDef * device_class;
device_class = getClass(req->wIndex);
switch (req->bmRequest & USB_REQ_TYPE_MASK) {
case USB_REQ_TYPE_CLASS :
if (req->wIndex < NUM_INTERFACES)
return USBD_Classes[req->wIndex]->Setup(pdev, req);
if (device_class != NULL)
return device_class->Setup(pdev, req);
else
return USBD_FAIL;
@ -236,7 +274,7 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType
switch (req->bRequest) {
case USB_REQ_GET_DESCRIPTOR :
for(i = 0; i < NUM_INTERFACES; i++) {
for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i]->Setup(pdev, req) != USBD_OK) {
return USBD_FAIL;
}
@ -246,8 +284,8 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType
case USB_REQ_GET_INTERFACE :
case USB_REQ_SET_INTERFACE :
if (req->wIndex < NUM_INTERFACES)
return USBD_Classes[req->wIndex]->Setup(pdev, req);
if (device_class != NULL)
return device_class->Setup(pdev, req);
else
return USBD_FAIL;
}
@ -274,7 +312,7 @@ static uint8_t USBD_Composite_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum)
static uint8_t USBD_Composite_EP0_RxReady (USBD_HandleTypeDef *pdev) {
int i;
for(i = 0; i < NUM_INTERFACES; i++) {
for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i]->EP0_RxReady != NULL) {
if (USBD_Classes[i]->EP0_RxReady(pdev) != USBD_OK) {
return USBD_FAIL;

View File

@ -342,6 +342,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
uint8_t *pbuf = NULL;
uint16_t status_info = 0U;
USBD_StatusTypeDef ret = USBD_OK;
req->wLength = req->wLength & 0x7f;
switch (req->bmRequest & USB_REQ_TYPE_MASK)
{
@ -386,6 +387,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
break;
case USB_REQ_GET_DESCRIPTOR:
req->wLength = req->wLength & 0x7f;
if(req->wValue >> 8 == HID_REPORT_DESC)
{
len = MIN(HID_FIDO_REPORT_DESC_SIZE , req->wLength);

View File

@ -270,7 +270,7 @@ void ams_print_int1(uint8_t int0)
#endif
}
void ams_init()
int 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);
@ -283,6 +283,13 @@ void ams_init()
// delay(10);
SELECT();
delay(1);
uint8_t productType = ams_read_reg(AMS_REG_PRODUCT_TYPE);
if (productType == 0x14)
{
return 1;
}
return 0;
}
void ams_configure()

View File

@ -39,7 +39,7 @@ typedef union
#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();
int ams_init();
void ams_configure();
void ams_read_buffer(uint8_t * data, int len);

View File

@ -23,6 +23,7 @@
//#define USING_DEV_BOARD
#define ENABLE_U2F_EXTENSIONS
// #define ENABLE_WALLET
#define ENABLE_U2F
@ -30,7 +31,7 @@
// #define DISABLE_CTAPHID_WINK
// #define DISABLE_CTAPHID_CBOR
#define ENABLE_SERIAL_PRINTING
// #define ENABLE_SERIAL_PRINTING
#if defined(SOLO_HACKER)
#define SOLO_PRODUCT_NAME "Solo Hacker " SOLO_VERSION

View File

@ -157,6 +157,11 @@ void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
key = master_secret;
klen = sizeof(master_secret)/2;
}
else if (key == CRYPTO_TRANSPORT_KEY2)
{
key = transport_secret;
klen = 32;
}
if(klen > 64)
@ -277,6 +282,11 @@ void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8
memmove(x,pubkey,32);
memmove(y,pubkey+32,32);
}
void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey)
{
uECC_compute_public_key(privkey, pubkey, _es256_curve);
}
void crypto_load_external_key(uint8_t * key, int len)
{

View File

@ -29,6 +29,7 @@
#include "usbd_cdc_if.h"
#include "nfc.h"
#include "init.h"
#include "sense.h"
#define LOW_FREQUENCY 1
#define HIGH_FREQUENCY 0
@ -37,13 +38,63 @@ void wait_for_usb_tether();
uint32_t __90_ms = 0;
uint32_t __last_button_press_time = 0;
uint32_t __last_button_bounce_time = 0;
uint32_t __device_status = 0;
uint32_t __last_update = 0;
extern PCD_HandleTypeDef hpcd;
static bool haveNFC = 0;
static int _NFC_status = 0;
static bool isLowFreq = 0;
static bool _RequestComeFromNFC = false;
#define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN))
// #define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN))
static int is_physical_button_pressed()
{
return (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN));
}
static int is_touch_button_pressed()
{
int is_pressed = (tsc_read_button(0) || tsc_read_button(1));
#ifndef IS_BOOTLOADER
if (is_pressed)
{
// delay for debounce, and longer than polling timer period.
delay(95);
return (tsc_read_button(0) || tsc_read_button(1));
}
#endif
return is_pressed;
}
int (*IS_BUTTON_PRESSED)() = is_physical_button_pressed;
static void edge_detect_touch_button()
{
static uint8_t last_touch = 0;
uint8_t current_touch = 0;
if (is_touch_button_pressed == IS_BUTTON_PRESSED)
{
current_touch = (tsc_read_button(0) || tsc_read_button(1));
// 1 sample per 25 ms
if ((millis() - __last_button_bounce_time) > 25)
{
// Detect "touch / rising edge"
if (!last_touch && current_touch)
{
__last_button_press_time = millis();
}
__last_button_bounce_time = millis();
last_touch = current_touch;
}
}
}
void request_from_nfc(bool request_active) {
_RequestComeFromNFC = request_active;
}
// Timer6 overflow handler. happens every ~90ms.
void TIM6_DAC_IRQHandler()
@ -51,22 +102,40 @@ void TIM6_DAC_IRQHandler()
// timer is only 16 bits, so roll it over here
TIM6->SR = 0;
__90_ms += 1;
if ((millis() - __last_update) > 8)
if ((millis() - __last_update) > 90)
{
if (__device_status != CTAPHID_STATUS_IDLE)
{
ctaphid_update_status(__device_status);
}
}
edge_detect_touch_button();
#ifndef IS_BOOTLOADER
// NFC sending WTX if needs
if (device_is_nfc())
if (device_is_nfc() == NFC_IS_ACTIVE)
{
WTX_timer_exec();
}
#endif
}
// Interrupt on rising edge of button (button released)
void EXTI0_IRQHandler(void)
{
EXTI->PR1 = EXTI->PR1;
if (is_physical_button_pressed == IS_BUTTON_PRESSED)
{
// Only allow 1 press per 25 ms.
if ((millis() - __last_button_bounce_time) > 25)
{
__last_button_press_time = millis();
}
__last_button_bounce_time = millis();
}
}
// Global USB interrupt handler
void USB_IRQHandler(void)
{
@ -107,23 +176,41 @@ void device_reboot()
NVIC_SystemReset();
}
void device_init()
void device_init_button()
{
if (tsc_sensor_exists())
{
tsc_init();
IS_BUTTON_PRESSED = is_touch_button_pressed;
}
else
{
IS_BUTTON_PRESSED = is_physical_button_pressed;
}
}
void device_init(int argc, char *argv[])
{
hw_init(LOW_FREQUENCY);
haveNFC = nfc_init();
if (! tsc_sensor_exists())
{
_NFC_status = nfc_init();
}
if (haveNFC)
if (_NFC_status == NFC_IS_ACTIVE)
{
printf1(TAG_NFC, "Have NFC\r\n");
isLowFreq = 1;
IS_BUTTON_PRESSED = is_physical_button_pressed;
}
else
{
printf1(TAG_NFC, "Have NO NFC\r\n");
hw_init(HIGH_FREQUENCY);
isLowFreq = 0;
device_init_button();
}
usbhid_init();
@ -139,9 +226,9 @@ void device_init()
}
bool device_is_nfc()
int device_is_nfc()
{
return haveNFC;
return _NFC_status;
}
void wait_for_usb_tether()
@ -433,7 +520,7 @@ void device_manage()
}
#endif
#ifndef IS_BOOTLOADER
// if(device_is_nfc())
if(device_is_nfc())
nfc_loop();
#endif
}
@ -457,13 +544,49 @@ static int handle_packets()
return 0;
}
int ctap_user_presence_test()
static int wait_for_button_activate(uint32_t wait)
{
int ret;
if (device_is_nfc())
uint32_t start = millis();
do
{
if ((start + wait) < millis())
{
return 0;
}
delay(1);
ret = handle_packets();
if (ret)
return ret;
} while (!IS_BUTTON_PRESSED());
return 0;
}
static int wait_for_button_release(uint32_t wait)
{
int ret;
uint32_t start = millis();
do
{
if ((start + wait) < millis())
{
return 0;
}
delay(1);
ret = handle_packets();
if (ret)
return ret;
} while (IS_BUTTON_PRESSED());
return 0;
}
int ctap_user_presence_test(uint32_t up_delay)
{
int ret;
if (device_is_nfc() == NFC_IS_ACTIVE || _RequestComeFromNFC)
{
return 1;
}
#if SKIP_BUTTON_CHECK_WITH_DELAY
int i=500;
while(i--)
@ -476,49 +599,41 @@ int ctap_user_presence_test()
#elif SKIP_BUTTON_CHECK_FAST
delay(2);
ret = handle_packets();
if (ret) return ret;
if (ret)
return ret;
goto done;
#endif
uint32_t t1 = millis();
// If button was pressed within last [2] seconds, succeed.
if (__last_button_press_time && (millis() - __last_button_press_time < 2000))
{
goto done;
}
// Set LED status and wait.
led_rgb(0xff3520);
while (IS_BUTTON_PRESSED())
{
if (t1 + 5000 < millis())
{
printf1(TAG_GEN,"Button not pressed\n");
goto fail;
}
ret = handle_packets();
// Block and wait for some time.
ret = wait_for_button_activate(up_delay);
if (ret) return ret;
}
t1 = millis();
do
{
if (t1 + 5000 < millis())
{
goto fail;
}
delay(1);
ret = handle_packets();
ret = wait_for_button_release(up_delay);
if (ret) return ret;
}
while (! IS_BUTTON_PRESSED());
led_rgb(0x001040);
delay(50);
// If button was pressed within last [2] seconds, succeed.
if (__last_button_press_time && (millis() - __last_button_press_time < 2000))
{
goto done;
}
return 0;
#if SKIP_BUTTON_CHECK_WITH_DELAY || SKIP_BUTTON_CHECK_FAST
done:
#endif
return 1;
ret = wait_for_button_release(up_delay);
__last_button_press_time = 0;
return 1;
fail:
return 0;
}
int ctap_generate_rng(uint8_t * dst, size_t num)

View File

@ -20,6 +20,7 @@
#include "stm32l4xx_ll_rng.h"
#include "stm32l4xx_ll_spi.h"
#include "stm32l4xx_ll_usb.h"
#include "stm32l4xx_ll_exti.h"
#include "stm32l4xx_hal_pcd.h"
#include "stm32l4xx_hal.h"
@ -31,6 +32,7 @@
#include "usbd_cdc_if.h"
#include "device.h"
#include "init.h"
#include "sense.h"
#include APP_CONFIG
// KHz
@ -94,8 +96,6 @@ void hw_init(int lowfreq)
SystemClock_Config();
}
if (!lowfreq)
{
init_pwm();
@ -108,6 +108,7 @@ void hw_init(int lowfreq)
#endif
init_rng();
init_spi();
}
@ -849,19 +850,17 @@ void init_gpio(void)
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);
#ifndef IS_BOOTLOADER
LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTA, LL_SYSCFG_EXTI_LINE0);
LL_EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_0; // GPIOA_0
EXTI_InitStruct.Line_32_63 = LL_EXTI_LINE_NONE;
EXTI_InitStruct.LineCommand = ENABLE;
EXTI_InitStruct.Mode = LL_EXTI_MODE_IT;
EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING;
LL_EXTI_Init(&EXTI_InitStruct);
NVIC_EnableIRQ(EXTI0_IRQn);
#endif
}

View File

@ -14,6 +14,21 @@
#define IS_IRQ_ACTIVE() (1 == (LL_GPIO_ReadInputPort(SOLO_AMS_IRQ_PORT) & SOLO_AMS_IRQ_PIN))
// chain buffer for 61XX responses
static uint8_t chain_buffer[2048] = {0};
static size_t chain_buffer_len = 0;
static bool chain_buffer_tx = false;
uint8_t p14443_block_offset(uint8_t pcb) {
uint8_t offset = 1;
// NAD following
if (pcb & 0x04) offset++;
// CID following
if (pcb & 0x08) offset++;
return offset;
}
// Capability container
const CAPABILITY_CONTAINER NFC_CC = {
.cclen_hi = 0x00, .cclen_lo = 0x0f,
@ -55,11 +70,12 @@ void nfc_state_init()
NFC_STATE.block_num = 1;
}
bool nfc_init()
int nfc_init()
{
uint32_t t1;
int init;
nfc_state_init();
ams_init();
init = ams_init();
// Detect if we are powered by NFC field by listening for a message for
// first 10 ms.
@ -67,28 +83,41 @@ bool nfc_init()
while ((millis() - t1) < 10)
{
if (nfc_loop() > 0)
return 1;
return NFC_IS_ACTIVE;
}
// Under USB power. Configure AMS chip.
ams_configure();
return 0;
if (init)
{
return NFC_IS_AVAILABLE;
}
return NFC_IS_NA;
}
static uint8_t gl_int0 = 0;
void process_int0(uint8_t int0)
{
gl_int0 = int0;
}
bool ams_wait_for_tx(uint32_t timeout_ms)
{
if (gl_int0 & AMS_INT_TXE) {
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
process_int0(int0);
return true;
}
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)
process_int0(int0);
if (int0 & AMS_INT_TXE || int0 & AMS_INT_RXE)
return true;
delay(1);
@ -105,7 +134,13 @@ bool ams_receive_with_timeout(uint32_t timeout_ms, uint8_t * data, int maxlen, i
uint32_t tstart = millis();
while (tstart + timeout_ms > millis())
{
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
uint8_t int0 = 0;
if (gl_int0 & AMS_INT_RXE) {
int0 = gl_int0;
} else {
int0 = ams_read_reg(AMS_REG_INT0);
process_int0(int0);
}
uint8_t buffer_status2 = ams_read_reg(AMS_REG_BUF2);
if (buffer_status2 && (int0 & AMS_INT_RXE))
@ -155,14 +190,25 @@ bool nfc_write_response_ex(uint8_t req0, uint8_t * data, uint8_t len, uint16_t r
if (len > 32 - 3)
return false;
res[0] = NFC_CMD_IBLOCK | (req0 & 3);
res[0] = NFC_CMD_IBLOCK | (req0 & 0x0f);
res[1] = 0;
res[2] = 0;
uint8_t block_offset = p14443_block_offset(req0);
if (len && data)
memcpy(&res[1], data, len);
memcpy(&res[block_offset], data, len);
res[len + 1] = resp >> 8;
res[len + 2] = resp & 0xff;
nfc_write_frame(res, 3 + len);
res[len + block_offset + 0] = resp >> 8;
res[len + block_offset + 1] = resp & 0xff;
nfc_write_frame(res, block_offset + len + 2);
if (!ams_wait_for_tx(1))
{
printf1(TAG_NFC, "TX resp timeout. len: %d \r\n", len);
return false;
}
return true;
}
@ -172,25 +218,28 @@ 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)
void nfc_write_response_chaining_plain(uint8_t req0, uint8_t * data, int len)
{
uint8_t res[32 + 2];
int sendlen = 0;
uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 3);
uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 0x0f);
uint8_t block_offset = p14443_block_offset(req0);
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);
memcpy(&res[block_offset], data, len);
nfc_write_frame(res, len + block_offset);
} else {
int sendlen = 0;
do {
// transmit I block
int vlen = MIN(31, len - sendlen);
int vlen = MIN(32 - block_offset, len - sendlen);
res[0] = iBlock;
memcpy(&res[1], &data[sendlen], vlen);
res[1] = 0;
res[2] = 0;
memcpy(&res[block_offset], &data[sendlen], vlen);
// if not a last block
if (vlen + sendlen < len)
@ -199,15 +248,15 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
}
// send data
nfc_write_frame(res, vlen + 1);
nfc_write_frame(res, vlen + block_offset);
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 (!ams_wait_for_tx(5))
{
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)
@ -220,9 +269,10 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
break;
}
if (reclen != 1)
uint8_t rblock_offset = p14443_block_offset(recbuf[0]);
if (reclen != rblock_offset)
{
printf1(TAG_NFC, "R block length error. len: %d. %d/%d \r\n", reclen,sendlen,len);
printf1(TAG_NFC, "R block length error. len: %d. %d/%d \r\n", reclen, sendlen, len);
dump_hex1(TAG_NFC, recbuf, reclen);
break;
}
@ -239,6 +289,38 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
}
}
void append_get_response(uint8_t *data, size_t rest_len)
{
data[0] = 0x61;
data[1] = 0x00;
if (rest_len <= 0xff)
data[1] = rest_len & 0xff;
}
void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len, bool extapdu)
{
chain_buffer_len = 0;
chain_buffer_tx = true;
// if we dont need to break data to parts that need to exchange via GET RESPONSE command (ISO 7816-4 7.1.3)
if (len <= 255 || extapdu)
{
nfc_write_response_chaining_plain(req0, data, len);
} else {
size_t pcklen = MIN(253, len);
chain_buffer_len = len - pcklen;
printf1(TAG_NFC, "61XX chaining %d/%d.\r\n", pcklen, chain_buffer_len);
memmove(chain_buffer, data, pcklen);
append_get_response(&chain_buffer[pcklen], chain_buffer_len);
nfc_write_response_chaining_plain(req0, chain_buffer, pcklen + 2); // 2 for 61XX
// put the rest data into chain buffer
memmove(chain_buffer, &data[pcklen], chain_buffer_len);
}
}
// WTX on/off:
// sends/receives WTX frame to reader every `WTX_time` time in ms
// works via timer interrupts
@ -291,7 +373,7 @@ bool WTX_off()
void WTX_timer_exec()
{
// condition: (timer on) or (not expired[300ms])
if ((WTX_timer <= 0) || WTX_timer + 300 > millis())
if ((WTX_timer == 0) || WTX_timer + 300 > millis())
return;
WTX_process(10);
@ -302,12 +384,12 @@ void WTX_timer_exec()
// 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)
{
uint8_t wtx[] = {0xf2, 0x01};
nfc_write_frame(wtx, sizeof(wtx));
WTX_sent = true;
return true;
@ -365,39 +447,72 @@ int answer_rats(uint8_t parameter)
nfc_write_frame(res, sizeof(res));
ams_wait_for_tx(10);
if (!ams_wait_for_tx(10))
{
printf1(TAG_NFC, "RATS TX timeout.\r\n");
ams_write_command(AMS_CMD_DEFAULT);
return 1;
}
return 0;
}
void rblock_acknowledge()
void rblock_acknowledge(uint8_t req0, bool ack)
{
uint8_t buf[32];
uint8_t buf[32] = {0};
uint8_t block_offset = p14443_block_offset(req0);
NFC_STATE.block_num = !NFC_STATE.block_num;
buf[0] = NFC_CMD_RBLOCK | NFC_STATE.block_num;
nfc_write_frame(buf,1);
buf[0] = NFC_CMD_RBLOCK | (req0 & 0x0f);
if (ack)
buf[0] |= NFC_CMD_RBLOCK_ACK;
nfc_write_frame(buf, block_offset);
}
// international AID = RID:PIX
// RID length == 5 bytes
// usually aid length must be between 5 and 16 bytes
int applet_cmp(uint8_t * aid, int len, uint8_t * const_aid, int const_len)
{
if (len > const_len)
return 10;
// if international AID
if ((const_aid[0] & 0xf0) == 0xa0)
{
if (len < 5)
return 11;
return memcmp(aid, const_aid, MIN(len, const_len));
} else {
if (len != const_len)
return 11;
return memcmp(aid, const_aid, const_len);
}
}
// 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)
if (applet_cmp(aid, len, (uint8_t *)AID_FIDO, sizeof(AID_FIDO) - 1) == 0)
{
NFC_STATE.selected_applet = APP_FIDO;
return APP_FIDO;
}
else if (memcmp(aid,AID_NDEF_TYPE_4,sizeof(AID_NDEF_TYPE_4)) == 0)
else if (applet_cmp(aid, len, (uint8_t *)AID_NDEF_TYPE_4, sizeof(AID_NDEF_TYPE_4) - 1) == 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)
else if (applet_cmp(aid, len, (uint8_t *)AID_CAPABILITY_CONTAINER, sizeof(AID_CAPABILITY_CONTAINER) - 1) == 0)
{
NFC_STATE.selected_applet = APP_CAPABILITY_CONTAINER;
return APP_CAPABILITY_CONTAINER;
}
else if (memcmp(aid,AID_NDEF_TAG,sizeof(AID_NDEF_TAG)) == 0)
else if (applet_cmp(aid, len, (uint8_t *)AID_NDEF_TAG, sizeof(AID_NDEF_TAG) - 1) == 0)
{
NFC_STATE.selected_applet = APP_NDEF_TAG;
return APP_NDEF_TAG;
@ -405,27 +520,71 @@ int select_applet(uint8_t * aid, int len)
return APP_NOTHING;
}
void nfc_process_iblock(uint8_t * buf, int len)
void apdu_process(uint8_t buf0, uint8_t *apduptr, APDU_STRUCT *apdu)
{
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;
uint16_t reslen;
printf1(TAG_NFC,"Iblock: ");
dump_hex1(TAG_NFC, buf, len);
// check CLA
if (apdu->cla != 0x00 && apdu->cla != 0x80) {
printf1(TAG_NFC, "Unknown CLA %02x\r\n", apdu->cla);
nfc_write_response(buf0, SW_CLA_INVALID);
return;
}
// TODO this needs to be organized better
switch(apdu->ins)
{
case APDU_INS_SELECT:
if (plen > len - 6)
// ISO 7816. 7.1 GET RESPONSE command
case APDU_GET_RESPONSE:
if (apdu->p1 != 0x00 || apdu->p2 != 0x00)
{
printf1(TAG_ERR, "Truncating APDU length %d\r\n", apdu->lc);
plen = len-6;
nfc_write_response(buf0, SW_INCORRECT_P1P2);
printf1(TAG_NFC, "P1 or P2 error\r\n");
return;
}
// too many bytes needs. 0x00 and 0x100 - any length
if (apdu->le != 0 && apdu->le != 0x100 && apdu->le > chain_buffer_len)
{
uint16_t wlresp = SW_WRONG_LENGTH; // here can be 6700, 6C00, 6FXX. but the most standard way - 67XX or 6700
if (chain_buffer_len <= 0xff)
wlresp += chain_buffer_len & 0xff;
nfc_write_response(buf0, wlresp);
printf1(TAG_NFC, "buffer length less than requesteds\r\n");
return;
}
// create temporary packet
uint8_t pck[255] = {0};
size_t pcklen = 253;
if (apdu->le)
pcklen = apdu->le;
if (pcklen > chain_buffer_len)
pcklen = chain_buffer_len;
printf1(TAG_NFC, "GET RESPONSE. pck len: %d buffer len: %d\r\n", pcklen, chain_buffer_len);
// create packet and add 61XX there if we have another portion(s) of data
memmove(pck, chain_buffer, pcklen);
size_t dlen = 0;
if (chain_buffer_len - pcklen)
{
append_get_response(&pck[pcklen], chain_buffer_len - pcklen);
dlen = 2;
}
// send
nfc_write_response_chaining_plain(buf0, pck, pcklen + dlen); // dlen for 61XX
// shift the buffer
chain_buffer_len -= pcklen;
memmove(chain_buffer, &chain_buffer[pcklen], chain_buffer_len);
break;
case APDU_INS_SELECT:
// if (apdu->p1 == 0 && apdu->p2 == 0x0c)
// {
// printf1(TAG_NFC,"Select NDEF\r\n");
@ -440,53 +599,49 @@ void nfc_process_iblock(uint8_t * buf, int len)
// }
// else
{
selected = select_applet(payload, plen);
selected = select_applet(apdu->data, apdu->lc);
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);
nfc_write_response_ex(buf0, (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);
nfc_write_response(buf0, 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);
nfc_write_response(buf0, SW_FILE_NOT_FOUND);
printf1(TAG_NFC, "NOT selected "); dump_hex1(TAG_NFC, apdu->data, apdu->lc);
}
}
break;
case APDU_FIDO_U2F_VERSION:
if (NFC_STATE.selected_applet != APP_FIDO) {
nfc_write_response(buf[0], SW_INS_INVALID);
nfc_write_response(buf0, 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);
u2f_request_nfc(apduptr, apdu->data, apdu->lc, &ctap_resp);
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
break;
case APDU_FIDO_U2F_REGISTER:
if (NFC_STATE.selected_applet != APP_FIDO) {
nfc_write_response(buf[0], SW_INS_INVALID);
nfc_write_response(buf0, SW_INS_INVALID);
break;
}
printf1(TAG_NFC, "U2F Register command.\r\n");
if (plen != 64)
if (apdu->lc != 64)
{
printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", plen);
nfc_write_response(buf[0], SW_WRONG_LENGTH);
printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", apdu->lc);
nfc_write_response(buf0, SW_WRONG_LENGTH);
return;
}
@ -496,66 +651,64 @@ void nfc_process_iblock(uint8_t * buf, int len)
// WTX_on(WTX_TIME_DEFAULT);
// SystemClock_Config_LF32();
// delay(300);
if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_FAST);;
u2f_request_nfc(&buf[1], len, &ctap_resp);
if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);;
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
u2f_request_nfc(apduptr, apdu->data, apdu->lc, &ctap_resp);
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
// if (!WTX_off())
// return;
printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
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);
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
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);
nfc_write_response(buf0, SW_INS_INVALID);
break;
}
printf1(TAG_NFC, "U2F Authenticate command.\r\n");
if (plen != 64 + 1 + buf[6 + 64])
if (apdu->lc != 64 + 1 + apdu->data[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);
printf1(TAG_NFC, "U2F Authenticate request length error. len=%d keyhlen=%d.\r\n", apdu->lc, apdu->data[64]);
nfc_write_response(buf0, SW_WRONG_LENGTH);
return;
}
timestamp();
// WTX_on(WTX_TIME_DEFAULT);
u2f_request_nfc(&buf[1], len, &ctap_resp);
u2f_request_nfc(apduptr, apdu->data, apdu->lc, &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);
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
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;
nfc_write_response(buf0, SW_INS_INVALID);
return;
}
printf1(TAG_NFC, "FIDO2 CTAP message. %d\r\n", timestamp());
WTX_on(WTX_TIME_DEFAULT);
// WTX_on(WTX_TIME_DEFAULT);
request_from_nfc(true);
ctap_response_init(&ctap_resp);
status = ctap_request(payload, plen, &ctap_resp);
if (!WTX_off())
return;
status = ctap_request(apdu->data, apdu->lc, &ctap_resp);
request_from_nfc(false);
// if (!WTX_off())
// return;
printf1(TAG_NFC, "CTAP resp: 0x%02<EFBFBD> len: %d\r\n", status, ctap_resp.length);
printf1(TAG_NFC, "CTAP resp: 0x%02x len: %d\r\n", status, ctap_resp.length);
if (status == CTAP1_ERR_SUCCESS)
{
@ -569,49 +722,104 @@ void nfc_process_iblock(uint8_t * buf, int len)
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);
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
printf1(TAG_NFC,"CTAP answered %d (took %d)\r\n", millis(), timestamp());
break;
case APDU_INS_READ_BINARY:
// response length
reslen = apdu->le & 0xffff;
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);
if (reslen == 0 || reslen > sizeof(NFC_CC))
reslen = sizeof(NFC_CC);
nfc_write_response_ex(buf0, (uint8_t *)&NFC_CC, reslen, 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);
if (reslen == 0 || reslen > sizeof(NDEF_SAMPLE) - 1)
reslen = sizeof(NDEF_SAMPLE) - 1;
nfc_write_response_ex(buf0, NDEF_SAMPLE, reslen, SW_SUCCESS);
ams_wait_for_tx(10);
break;
default:
nfc_write_response(buf0, SW_FILE_NOT_FOUND);
printf1(TAG_ERR, "No binary applet selected!\r\n");
return;
break;
}
break;
case APDU_SOLO_RESET:
if (apdu->lc == 4 && !memcmp(apdu->data, "\x12\x56\xab\xf0", 4)) {
printf1(TAG_NFC, "Reset...\r\n");
nfc_write_response(buf0, SW_SUCCESS);
delay(20);
device_reboot();
while(1);
} else {
printf1(TAG_NFC, "Reset FAIL\r\n");
nfc_write_response(buf0, SW_INS_INVALID);
}
break;
default:
printf1(TAG_NFC, "Unknown INS %02x\r\n", apdu->ins);
nfc_write_response(buf[0], SW_INS_INVALID);
nfc_write_response(buf0, SW_INS_INVALID);
break;
}
}
void nfc_process_iblock(uint8_t * buf, int len)
{
uint8_t block_offset = p14443_block_offset(buf[0]);
// clear tx chain buffer if we have some other command than GET RESPONSE
if (chain_buffer_tx && buf[block_offset + 1] != APDU_GET_RESPONSE) {
chain_buffer_len = 0;
chain_buffer_tx = false;
}
APDU_STRUCT apdu;
if (apdu_decode(buf + block_offset, len - block_offset, &apdu)) {
printf1(TAG_NFC,"apdu decode error\r\n");
nfc_write_response(buf[0], SW_COND_USE_NOT_SATISFIED);
return;
}
printf1(TAG_NFC,"apdu ok. %scase=%02x cla=%02x ins=%02x p1=%02x p2=%02x lc=%d le=%d\r\n",
apdu.extended_apdu ? "[e]":"", apdu.case_type, apdu.cla, apdu.ins, apdu.p1, apdu.p2, apdu.lc, apdu.le);
// APDU level chaining. ISO7816-4, 5.1.1. class byte
if (!chain_buffer_tx && buf[block_offset] & 0x10) {
if (chain_buffer_len + len > sizeof(chain_buffer)) {
nfc_write_response(buf[0], SW_WRONG_LENGTH);
return;
}
memmove(&chain_buffer[chain_buffer_len], apdu.data, apdu.lc);
chain_buffer_len += apdu.lc;
nfc_write_response(buf[0], SW_SUCCESS);
printf1(TAG_NFC, "APDU chaining ok. %d/%d\r\n", apdu.lc, chain_buffer_len);
return;
}
// if we have ISO 7816 APDU chain - move there all the data
if (!chain_buffer_tx && chain_buffer_len > 0) {
memmove(&apdu.data[chain_buffer_len], apdu.data, apdu.lc);
memmove(apdu.data, chain_buffer, chain_buffer_len);
apdu.lc += chain_buffer_len; // here apdu struct does not match with memory!
printf1(TAG_NFC, "APDU chaining merge. %d/%d\r\n", chain_buffer_len, apdu.lc);
}
apdu_process(buf[0], &buf[block_offset], &apdu);
printf1(TAG_NFC,"prev.Iblock: ");
dump_hex1(TAG_NFC, buf, len);
}
static uint8_t ibuf[1024];
@ -625,19 +833,27 @@ void clear_ibuf()
void nfc_process_block(uint8_t * buf, unsigned int len)
{
printf1(TAG_NFC, "-----\r\n");
if (!len)
return;
if (IS_PPSS_CMD(buf[0]))
{
printf1(TAG_NFC, "NFC_CMD_PPSS\r\n");
printf1(TAG_NFC, "NFC_CMD_PPSS [%d] 0x%02x\r\n", len, (len > 2) ? buf[2] : 0);
if (buf[1] == 0x11 && (buf[2] & 0x0f) == 0x00) {
nfc_write_frame(buf, 1); // ack with correct start byte
} else {
printf1(TAG_NFC, "NFC_CMD_PPSS ERROR!!!\r\n");
nfc_write_frame((uint8_t*)"\x00", 1); // this should not happend. but iso14443-4 dont have NACK here, so just 0x00
}
}
else if (IS_IBLOCK(buf[0]))
{
uint8_t block_offset = p14443_block_offset(buf[0]);
if (buf[0] & 0x10)
{
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining blen=%d len=%d\r\n", ibuflen, len);
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining blen=%d len=%d offs=%d\r\n", ibuflen, len, block_offset);
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));
@ -648,44 +864,44 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
printf1(TAG_NFC_APDU,"i> ");
dump_hex1(TAG_NFC_APDU, buf, len);
if (len)
if (len > block_offset)
{
memcpy(&ibuf[ibuflen], &buf[1], len - 1);
ibuflen += len - 1;
memcpy(&ibuf[ibuflen], &buf[block_offset], len - block_offset);
ibuflen += len - block_offset;
}
// send R block
uint8_t rb = NFC_CMD_RBLOCK | NFC_CMD_RBLOCK_ACK | (buf[0] & 3);
nfc_write_frame(&rb, 1);
rblock_acknowledge(buf[0], true);
} else {
if (ibuflen)
{
if (len)
if (len > block_offset)
{
memcpy(&ibuf[ibuflen], &buf[1], len - 1);
ibuflen += len - 1;
memcpy(&ibuf[ibuflen], &buf[block_offset], len - block_offset);
ibuflen += len - block_offset;
}
memmove(&ibuf[1], ibuf, ibuflen);
ibuf[0] = buf[0];
ibuflen++;
// add last chaining to top of the block
memmove(&ibuf[block_offset], ibuf, ibuflen);
memmove(ibuf, buf, block_offset);
ibuflen += block_offset;
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining last block. blen=%d len=%d\r\n", ibuflen, len);
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining last block. blen=%d len=%d offset=%d\r\n", ibuflen, len, block_offset);
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);
memcpy(ibuf, buf, len); // because buf only 32b
nfc_process_iblock(ibuf, len);
}
clear_ibuf();
}
}
else if (IS_RBLOCK(buf[0]))
{
rblock_acknowledge();
rblock_acknowledge(buf[0], false);
printf1(TAG_NFC, "NFC_CMD_RBLOCK\r\n");
}
else if (IS_SBLOCK(buf[0]))
@ -704,6 +920,7 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
else
{
printf1(TAG_NFC, "NFC_CMD_SBLOCK, Unknown. len[%d]\r\n", len);
nfc_write_response(buf[0], SW_COND_USE_NOT_SATISFIED);
}
dump_hex1(TAG_NFC, buf, len);
}
@ -722,6 +939,8 @@ int nfc_loop()
read_reg_block(&ams);
uint8_t old_int0 = gl_int0;
process_int0(ams.regs.int0);
uint8_t state = AMS_STATE_MASK & ams.regs.rfid_status;
if (state != AMS_STATE_SELECTED && state != AMS_STATE_SELECTEDX)
@ -735,7 +954,7 @@ int nfc_loop()
// 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)
if (ams.regs.int0 & AMS_INT_INIT || old_int0 & AMS_INT_INIT)
{
nfc_state_init();
}
@ -744,7 +963,7 @@ int nfc_loop()
// ams_print_int1(ams.regs.int1);
}
if ((ams.regs.int0 & AMS_INT_RXE))
if (ams.regs.int0 & AMS_INT_RXE || old_int0 & AMS_INT_RXE)
{
if (ams.regs.buffer_status2)
{
@ -773,6 +992,7 @@ int nfc_loop()
printf1(TAG_NFC, "NFC_CMD_WUPA\r\n");
break;
case NFC_CMD_HLTA:
ams_write_command(AMS_CMD_SLEEP);
printf1(TAG_NFC, "HLTA/Halt\r\n");
break;
case NFC_CMD_RATS:

View File

@ -8,7 +8,7 @@
// Return number of bytes read if any.
int nfc_loop();
bool nfc_init();
int nfc_init();
typedef struct
{
@ -40,6 +40,8 @@ typedef struct
#define NFC_CMD_SBLOCK 0xc0
#define IS_SBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_SBLOCK) && (((x) & 0x02) == 0x02) )
extern uint8_t p14443_block_offset(uint8_t pcb);
#define NFC_SBLOCK_DESELECT 0x30
#define NFC_SBLOCK_WTX 0x30

View File

@ -0,0 +1,136 @@
#include "sense.h"
#include "device.h"
#include "log.h"
#include "stm32l4xx_ll_gpio.h"
#include "stm32l4xx_hal_tsc.h"
#define ELECTRODE_0 TSC_GROUP2_IO1
#define ELECTRODE_1 TSC_GROUP2_IO2
void tsc_init()
{
LL_GPIO_InitTypeDef GPIO_InitStruct;
// Enable TSC clock
RCC->AHB1ENR |= (1<<16);
/** TSC GPIO Configuration
PA4 ------> Channel 1
PA5 ------> Channel 2
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_5|LL_GPIO_PIN_4;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_9;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/** TSC GPIO Configuration
PA6 ------> sampling cap
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_6;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// Channel IOs
uint32_t channel_ios = TSC_GROUP2_IO1 | TSC_GROUP2_IO2;
// enable
TSC->CR = TSC_CR_TSCE;
TSC->CR |= (TSC_CTPH_8CYCLES |
TSC_CTPL_10CYCLES |
(uint32_t)(1 << TSC_CR_SSD_Pos) |
TSC_SS_PRESC_DIV1 |
TSC_PG_PRESC_DIV16 |
TSC_MCV_16383 |
TSC_SYNC_POLARITY_FALLING |
TSC_ACQ_MODE_NORMAL);
// Spread spectrum
if (0)
{
TSC->CR |= TSC_CR_SSE;
}
// Schmitt trigger and hysteresis
TSC->IOHCR = (uint32_t)(~(channel_ios | 0 | TSC_GROUP2_IO3));
// Sampling IOs
TSC->IOSCR = TSC_GROUP2_IO3;
// Groups
uint32_t grps = 0x02;
TSC->IOGCSR = grps;
TSC->IER &= (uint32_t)(~(TSC_IT_EOA | TSC_IT_MCE));
TSC->ICR = (TSC_FLAG_EOA | TSC_FLAG_MCE);
}
void tsc_set_electrode(uint32_t channel_ids)
{
TSC->IOCCR = (channel_ids);
}
void tsc_start_acq()
{
TSC->CR &= ~(TSC_CR_START);
TSC->ICR = TSC_FLAG_EOA | TSC_FLAG_MCE;
// Set IO output to output push-pull low
TSC->CR &= (~TSC_CR_IODEF);
TSC->CR |= TSC_CR_START;
}
void tsc_wait_on_acq()
{
while ( ! (TSC->ISR & TSC_FLAG_EOA) )
;
if ( TSC->ISR & TSC_FLAG_MCE )
{
printf1(TAG_ERR,"Max count reached\r\n");
}
}
uint32_t tsc_read(uint32_t indx)
{
return TSC->IOGXCR[indx];
}
uint32_t tsc_read_button(uint32_t index)
{
switch(index)
{
case 0:
tsc_set_electrode(ELECTRODE_0);
break;
case 1:
tsc_set_electrode(ELECTRODE_1);
break;
}
tsc_start_acq();
tsc_wait_on_acq();
return tsc_read(1) < 45;
}
int tsc_sensor_exists()
{
static uint8_t does = 0;
if (does) return 1;
LL_GPIO_SetPinMode(GPIOB, (1 << 1), LL_GPIO_MODE_INPUT);
LL_GPIO_SetPinPull(GPIOB, (1 << 1), LL_GPIO_PULL_UP);
// Short delay before reading pin
asm("nop"); asm("nop"); asm("nop"); asm("nop");
does = (LL_GPIO_ReadInputPort(GPIOB) & (1 << 1)) == 0;
LL_GPIO_SetPinPull(GPIOB, 1, LL_GPIO_PULL_NO);
return does;
}

View File

@ -0,0 +1,14 @@
#ifndef _SENSE_H_
#define _SENSE_H_
#include <stdint.h>
void tsc_init();
int tsc_sensor_exists();
// Read button0 or button1
// Returns 1 if pressed, 0 if not.
uint32_t tsc_read_button(uint32_t index);
#endif

65
tools/gadgetfs/Makefile Normal file
View File

@ -0,0 +1,65 @@
TOP := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
KERNEL_FULL_VERSION := $(shell uname -r)
KERNEL_VERSION := $(shell uname -r | grep -o "^[^-]*")
KERNEL_MAJOR := $(shell uname -r | cut -d. -f1)
KERNEL_MINOR := $(shell uname -r | cut -d. -f2)
MANUFACTURER = "Solo"
SERIAL = "1234567890"
IDVENDOR = "0x0483"
IDPRODUCT = "0xa2ca"
PRODUCT = "Solo Software Authenticator"
CONFIGFS = /sys/kernel/config
CONFIGFS_FIDO2 = $(CONFIGFS)/usb_gadget/fido2
obj-m := dummy_hcd.o
KVERSION := $(shell uname -r)
SHELL := /bin/bash
all: dummy_hcd.ko
install: dummy_hcd.ko
modprobe libcomposite
insmod dummy_hcd.ko
mkdir -p $(CONFIGFS_FIDO2)
mkdir -p $(CONFIGFS_FIDO2)/configs/c.1
mkdir -p $(CONFIGFS_FIDO2)/functions/hid.usb0
echo 0 > $(CONFIGFS_FIDO2)/functions/hid.usb0/protocol
echo 0 > $(CONFIGFS_FIDO2)/functions/hid.usb0/subclass
echo 64 > $(CONFIGFS_FIDO2)/functions/hid.usb0/report_length
echo -ne "\x06\xd0\xf1\x09\x01\xa1\x01\x09\x20\x15\x00\x26\xff\x00\x75\x08\x95\x40\x81\x02\x09\x21\x15\x00\x26\xff\x00\x75\x08\x95\x40\x91\x02\xc0" > $(CONFIGFS_FIDO2)/functions/hid.usb0/report_desc
mkdir $(CONFIGFS_FIDO2)/strings/0x409
mkdir $(CONFIGFS_FIDO2)/configs/c.1/strings/0x409
echo $(IDPRODUCT) > $(CONFIGFS_FIDO2)/idProduct
echo $(IDVENDOR) > $(CONFIGFS_FIDO2)/idVendor
echo $(SERIAL) > $(CONFIGFS_FIDO2)/strings/0x409/serialnumber
echo $(MANUFACTURER) > $(CONFIGFS_FIDO2)/strings/0x409/manufacturer
echo $(PRODUCT) > $(CONFIGFS_FIDO2)/strings/0x409/product
echo "Configuration 1" > $(CONFIGFS_FIDO2)/configs/c.1/strings/0x409/configuration
echo 120 > $(CONFIGFS_FIDO2)/configs/c.1/MaxPower
ln -s $(CONFIGFS_FIDO2)/functions/hid.usb0 $(CONFIGFS_FIDO2)/configs/c.1
echo "dummy_udc.0" > $(CONFIGFS_FIDO2)/UDC
uninstall:
echo "" > $(CONFIGFS_FIDO2)/UDC
rm $(CONFIGFS_FIDO2)/configs/c.1/hid.usb0
rmdir $(CONFIGFS_FIDO2)/configs/c.1/strings/0x409
rmdir $(CONFIGFS_FIDO2)/configs/c.1
rmdir $(CONFIGFS_FIDO2)/functions/hid.usb0
rmdir $(CONFIGFS_FIDO2)/strings/0x409
rmdir $(CONFIGFS_FIDO2)
rmmod dummy_hcd.ko
dummy_hcd.ko: dummy_hcd.c
$(MAKE) -C /lib/modules/$(KERNEL_FULL_VERSION)/build M=$(TOP) modules
dummy_hcd.c: /usr/src/linux-source-$(KERNEL_VERSION).tar.bz2
tar -xvf $^ linux-source-$(KERNEL_VERSION)/drivers/usb/gadget/udc/dummy_hcd.c
cp linux-source-$(KERNEL_VERSION)/drivers/usb/gadget/udc/dummy_hcd.c $@
clean:
$(MAKE) -C /lib/modules/$(KERNEL_FULL_VERSION)/build M=$(TOP) clean
rm -rf linux-source-$(KERNEL_VERSION)
rm -f dummy_hcd.c

View File

@ -1,58 +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
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

@ -1,11 +0,0 @@
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

File diff suppressed because it is too large Load Diff

View File

@ -1,252 +0,0 @@
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

@ -1,70 +0,0 @@
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

@ -1,204 +0,0 @@
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, catch=None):
self.msg = msg
self.catch = catch
def __enter__(self,):
print(self.msg)
def __exit__(self, a, b, c):
if self.catch is None:
print("Pass")
elif isinstance(b, self.catch):
print("Pass")
return b
else:
raise RuntimeError(f"Expected exception {self.catch} did not occur.")
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)

View File

@ -1,121 +0,0 @@
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

@ -1,12 +0,0 @@
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

@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDxXjGjdpW8N2/3
s1pYCe1LgVEJlnv0sz/iUaVsd2Z6hv5khaJPVc/LBl5uR5IzotLVw/fp9CU4r7wL
xkwezpH+zyo7VjlulRxUZ2zaclYyiKuE1LpdYmuLetzkDI/e2CF26eCQv3vbqxPy
dS9wplbgplBax6s8IlfjEhpvIxb4JW1+g7U+zyYGTYVakJYzvzTTl3IFqtO34BBE
vSJFDtBuZViwfEctFsinjMHXBo6Nb7OoCQq0ih0gZJwKcojYTbpbm2YhNR2i8mHy
6jpsv3bJTygJHIEDGILgu5T6uNY5nwMC4Js/w4YnI+kO4kmwAEEFzxZXa666UxZV
VbkWMUezAgMBAAECggEBANr5yuzzC9uLLAC8ba1LrEYBF0Usil6xNLcdvYePN6DX
0fnepyd0waT+rTM9qW1BPigDt2pAPniULnU8PRkB7cAPsM/OZSJnEyGcB0yTyJHm
Hj3PRRitzsXm/HnEz82rpYidnEeWAUeEiP9Bw31e25jKKkKBvV88BSIT7XmAizkp
8H/ya+qm3R8Kxsr/t19UbkrBJ0V9OIM6GXsCZHul2EsRoY7d4om90IvKCHW5MIuT
MPkIR2msntgNIhYh+mxDrcZ2qq/AZO/GGf3GpxiDN4imuNvgd6HVg0qwxIpblhjK
qX1AeiLQ+ljlz3ktY4TVpt2vgPToekKbme6neCFD3FkCgYEA+4E5UegMTcLce6jR
6JMxA5PeING7zRLLKwNsgZvwdpEXh5OBMxWu674tg+OTWG3fPNaIdz+PN+DJFtoV
/dDNkOG5TCwBQJPipN2Y5bSmjbBDI4jb/rDLeUvFFF/Hp81bMDYe+o1DNomzkC+A
5/uGeNXET90D5NpgRU8Jk0gKlNcCgYEA9a6WmBHWZctXNqD9Sx9pw5i5RQyy2s/D
2PmZ01VcE6uMZGghHw4gJIzpD1bY2nP9g8yD5v/VD0bz+GTBp3eKlw/9E1aYfMwN
gUdP2sqgnYI7gareI/DTONBVQEmDcRTCH6fewgnwg9wuCwoqgxGAoi9IX4vBWFJt
YNlOYQErLIUCgYAVtJlV0Ej/jQmqQm+bOtjIDkLlYjRrBmwyUiFTLjoagXseYESO
PBjUj50t/L4Cq7jQb1NntzyM/gFcz4WGWjbjgheT01hoUlsFD3ramDSnlca1kmIq
IOful/NyRrHccYSlLIaP6REb69ZrYy4k1zhLxWcj3VcwsQgN8zxIUbdYEQKBgQCL
APUDpVQA1EPMDNpDHsrgeBCbGMw5MURGBzMZdzpZhr4wMRpMT9mv1Goo26JmNypA
3/3hPO53blWrPJa1AdXQEqPFxUERmwIpGwf7apnlhEHW4647944KnxUdAnr0CCKt
dnV6o9UJRhJm/KGA9u4o1UfFh3UlW723BjxqdhbPYQKBgF4KQtcw/Y2kF2BIerD6
LyO/tbS90VMQiZYZjGmuLG0K351RqW/l5U9RaEK2hpb10Qu7x/inmSM7sBY7NWE8
YOJLH/ArubTCLwiFzkQp24NFZt19xj2qlAunydhgus+q+r5mCp18QyjhTdMYVFqk
3biBiaWbI3HoS3qyCf7ezWvw
-----END PRIVATE KEY-----

View File

@ -1 +0,0 @@
D7564E117010E076

View File

@ -1,23 +0,0 @@
<html>
<head>
</head>
<body>
<h1>U2F Bridge Demo</h1>
<h3 id="progress"></h3>
<h3 id="errors" style="color:red;"></h3>
<p>To update firmware, upload JSON file here.</p>
<input type="file" id="input" onchange="handleFirmware(this.files)">
<br>
<br>
<button onclick="run_tests()">Run tests</button>
</body>
<script src="js/u2f-api.js"></script>
<script src="js/elliptic.js"></script>
<script src="js/sha256.min.js"></script>
<script src="js/aes.js"></script>
<script src="js/intel-hex.js"></script>
<script src="js/wallet.js"></script>
</html>

View File

@ -1,803 +0,0 @@
"use strict";
(function(root) {
function checkInt(value) {
return (parseInt(value) === value);
}
function checkInts(arrayish) {
if (!checkInt(arrayish.length)) { return false; }
for (var i = 0; i < arrayish.length; i++) {
if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
return false;
}
}
return true;
}
function coerceArray(arg, copy) {
// ArrayBuffer view
if (arg.buffer && ArrayBuffer.isView(arg) && arg.name === 'Uint8Array') {
if (copy) {
if (arg.slice) {
arg = arg.slice();
} else {
arg = Array.prototype.slice.call(arg);
}
}
return arg;
}
// It's an array; check it is a valid representation of a byte
if (Array.isArray(arg)) {
if (!checkInts(arg)) {
throw new Error('Array contains invalid value: ' + arg);
}
return new Uint8Array(arg);
}
// Something else, but behaves like an array (maybe a Buffer? Arguments?)
if (checkInt(arg.length) && checkInts(arg)) {
return new Uint8Array(arg);
}
throw new Error('unsupported array-like object');
}
function createArray(length) {
return new Uint8Array(length);
}
function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) {
if (sourceStart != null || sourceEnd != null) {
if (sourceArray.slice) {
sourceArray = sourceArray.slice(sourceStart, sourceEnd);
} else {
sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd);
}
}
targetArray.set(sourceArray, targetStart);
}
var convertUtf8 = (function() {
function toBytes(text) {
var result = [], i = 0;
text = encodeURI(text);
while (i < text.length) {
var c = text.charCodeAt(i++);
// if it is a % sign, encode the following 2 bytes as a hex value
if (c === 37) {
result.push(parseInt(text.substr(i, 2), 16))
i += 2;
// otherwise, just the actual byte
} else {
result.push(c)
}
}
return coerceArray(result);
}
function fromBytes(bytes) {
var result = [], i = 0;
while (i < bytes.length) {
var c = bytes[i];
if (c < 128) {
result.push(String.fromCharCode(c));
i++;
} else if (c > 191 && c < 224) {
result.push(String.fromCharCode(((c & 0x1f) << 6) | (bytes[i + 1] & 0x3f)));
i += 2;
} else {
result.push(String.fromCharCode(((c & 0x0f) << 12) | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f)));
i += 3;
}
}
return result.join('');
}
return {
toBytes: toBytes,
fromBytes: fromBytes,
}
})();
var convertHex = (function() {
function toBytes(text) {
var result = [];
for (var i = 0; i < text.length; i += 2) {
result.push(parseInt(text.substr(i, 2), 16));
}
return result;
}
// http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
var Hex = '0123456789abcdef';
function fromBytes(bytes) {
var result = [];
for (var i = 0; i < bytes.length; i++) {
var v = bytes[i];
result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
}
return result.join('');
}
return {
toBytes: toBytes,
fromBytes: fromBytes,
}
})();
// Number of rounds by keysize
var numberOfRounds = {16: 10, 24: 12, 32: 14}
// Round constant words
var rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91];
// S-box and Inverse S-box (S is for Substitution)
var S = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16];
var Si =[0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d];
// Transformations for encryption
var T1 = [0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a];
var T2 = [0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616];
var T3 = [0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16];
var T4 = [0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c];
// Transformations for decryption
var T5 = [0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742];
var T6 = [0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857];
var T7 = [0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8];
var T8 = [0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0];
// Transformations for decryption key expansion
var U1 = [0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3];
var U2 = [0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697];
var U3 = [0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46];
var U4 = [0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d];
function convertToInt32(bytes) {
var result = [];
for (var i = 0; i < bytes.length; i += 4) {
result.push(
(bytes[i ] << 24) |
(bytes[i + 1] << 16) |
(bytes[i + 2] << 8) |
bytes[i + 3]
);
}
return result;
}
var AES = function(key) {
if (!(this instanceof AES)) {
throw Error('AES must be instanitated with `new`');
}
Object.defineProperty(this, 'key', {
value: coerceArray(key, true)
});
this._prepare();
}
AES.prototype._prepare = function() {
var rounds = numberOfRounds[this.key.length];
if (rounds == null) {
throw new Error('invalid key size (must be 16, 24 or 32 bytes)');
}
// encryption round keys
this._Ke = [];
// decryption round keys
this._Kd = [];
for (var i = 0; i <= rounds; i++) {
this._Ke.push([0, 0, 0, 0]);
this._Kd.push([0, 0, 0, 0]);
}
var roundKeyCount = (rounds + 1) * 4;
var KC = this.key.length / 4;
// convert the key into ints
var tk = convertToInt32(this.key);
// copy values into round key arrays
var index;
for (var i = 0; i < KC; i++) {
index = i >> 2;
this._Ke[index][i % 4] = tk[i];
this._Kd[rounds - index][i % 4] = tk[i];
}
// key expansion (fips-197 section 5.2)
var rconpointer = 0;
var t = KC, tt;
while (t < roundKeyCount) {
tt = tk[KC - 1];
tk[0] ^= ((S[(tt >> 16) & 0xFF] << 24) ^
(S[(tt >> 8) & 0xFF] << 16) ^
(S[ tt & 0xFF] << 8) ^
S[(tt >> 24) & 0xFF] ^
(rcon[rconpointer] << 24));
rconpointer += 1;
// key expansion (for non-256 bit)
if (KC != 8) {
for (var i = 1; i < KC; i++) {
tk[i] ^= tk[i - 1];
}
// key expansion for 256-bit keys is "slightly different" (fips-197)
} else {
for (var i = 1; i < (KC / 2); i++) {
tk[i] ^= tk[i - 1];
}
tt = tk[(KC / 2) - 1];
tk[KC / 2] ^= (S[ tt & 0xFF] ^
(S[(tt >> 8) & 0xFF] << 8) ^
(S[(tt >> 16) & 0xFF] << 16) ^
(S[(tt >> 24) & 0xFF] << 24));
for (var i = (KC / 2) + 1; i < KC; i++) {
tk[i] ^= tk[i - 1];
}
}
// copy values into round key arrays
var i = 0, r, c;
while (i < KC && t < roundKeyCount) {
r = t >> 2;
c = t % 4;
this._Ke[r][c] = tk[i];
this._Kd[rounds - r][c] = tk[i++];
t++;
}
}
// inverse-cipher-ify the decryption round key (fips-197 section 5.3)
for (var r = 1; r < rounds; r++) {
for (var c = 0; c < 4; c++) {
tt = this._Kd[r][c];
this._Kd[r][c] = (U1[(tt >> 24) & 0xFF] ^
U2[(tt >> 16) & 0xFF] ^
U3[(tt >> 8) & 0xFF] ^
U4[ tt & 0xFF]);
}
}
}
AES.prototype.encrypt = function(plaintext) {
if (plaintext.length != 16) {
throw new Error('invalid plaintext size (must be 16 bytes)');
}
var rounds = this._Ke.length - 1;
var a = [0, 0, 0, 0];
// convert plaintext to (ints ^ key)
var t = convertToInt32(plaintext);
for (var i = 0; i < 4; i++) {
t[i] ^= this._Ke[0][i];
}
// apply round transforms
for (var r = 1; r < rounds; r++) {
for (var i = 0; i < 4; i++) {
a[i] = (T1[(t[ i ] >> 24) & 0xff] ^
T2[(t[(i + 1) % 4] >> 16) & 0xff] ^
T3[(t[(i + 2) % 4] >> 8) & 0xff] ^
T4[ t[(i + 3) % 4] & 0xff] ^
this._Ke[r][i]);
}
t = a.slice();
}
// the last round is special
var result = createArray(16), tt;
for (var i = 0; i < 4; i++) {
tt = this._Ke[rounds][i];
result[4 * i ] = (S[(t[ i ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
result[4 * i + 1] = (S[(t[(i + 1) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
result[4 * i + 2] = (S[(t[(i + 2) % 4] >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
result[4 * i + 3] = (S[ t[(i + 3) % 4] & 0xff] ^ tt ) & 0xff;
}
return result;
}
AES.prototype.decrypt = function(ciphertext) {
if (ciphertext.length != 16) {
throw new Error('invalid ciphertext size (must be 16 bytes)');
}
var rounds = this._Kd.length - 1;
var a = [0, 0, 0, 0];
// convert plaintext to (ints ^ key)
var t = convertToInt32(ciphertext);
for (var i = 0; i < 4; i++) {
t[i] ^= this._Kd[0][i];
}
// apply round transforms
for (var r = 1; r < rounds; r++) {
for (var i = 0; i < 4; i++) {
a[i] = (T5[(t[ i ] >> 24) & 0xff] ^
T6[(t[(i + 3) % 4] >> 16) & 0xff] ^
T7[(t[(i + 2) % 4] >> 8) & 0xff] ^
T8[ t[(i + 1) % 4] & 0xff] ^
this._Kd[r][i]);
}
t = a.slice();
}
// the last round is special
var result = createArray(16), tt;
for (var i = 0; i < 4; i++) {
tt = this._Kd[rounds][i];
result[4 * i ] = (Si[(t[ i ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
result[4 * i + 1] = (Si[(t[(i + 3) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
result[4 * i + 2] = (Si[(t[(i + 2) % 4] >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
result[4 * i + 3] = (Si[ t[(i + 1) % 4] & 0xff] ^ tt ) & 0xff;
}
return result;
}
/**
* Mode Of Operation - Electonic Codebook (ECB)
*/
var ModeOfOperationECB = function(key) {
if (!(this instanceof ModeOfOperationECB)) {
throw Error('AES must be instanitated with `new`');
}
this.description = "Electronic Code Block";
this.name = "ecb";
this._aes = new AES(key);
}
ModeOfOperationECB.prototype.encrypt = function(plaintext) {
plaintext = coerceArray(plaintext);
if ((plaintext.length % 16) !== 0) {
throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
}
var ciphertext = createArray(plaintext.length);
var block = createArray(16);
for (var i = 0; i < plaintext.length; i += 16) {
copyArray(plaintext, block, 0, i, i + 16);
block = this._aes.encrypt(block);
copyArray(block, ciphertext, i);
}
return ciphertext;
}
ModeOfOperationECB.prototype.decrypt = function(ciphertext) {
ciphertext = coerceArray(ciphertext);
if ((ciphertext.length % 16) !== 0) {
throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
}
var plaintext = createArray(ciphertext.length);
var block = createArray(16);
for (var i = 0; i < ciphertext.length; i += 16) {
copyArray(ciphertext, block, 0, i, i + 16);
block = this._aes.decrypt(block);
copyArray(block, plaintext, i);
}
return plaintext;
}
/**
* Mode Of Operation - Cipher Block Chaining (CBC)
*/
var ModeOfOperationCBC = function(key, iv) {
if (!(this instanceof ModeOfOperationCBC)) {
throw Error('AES must be instanitated with `new`');
}
this.description = "Cipher Block Chaining";
this.name = "cbc";
if (!iv) {
iv = createArray(16);
} else if (iv.length != 16) {
throw new Error('invalid initialation vector size (must be 16 bytes)');
}
this._lastCipherblock = coerceArray(iv, true);
this._aes = new AES(key);
}
ModeOfOperationCBC.prototype.encrypt = function(plaintext) {
plaintext = coerceArray(plaintext);
if ((plaintext.length % 16) !== 0) {
throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
}
var ciphertext = createArray(plaintext.length);
var block = createArray(16);
for (var i = 0; i < plaintext.length; i += 16) {
copyArray(plaintext, block, 0, i, i + 16);
for (var j = 0; j < 16; j++) {
block[j] ^= this._lastCipherblock[j];
}
this._lastCipherblock = this._aes.encrypt(block);
copyArray(this._lastCipherblock, ciphertext, i);
}
return ciphertext;
}
ModeOfOperationCBC.prototype.decrypt = function(ciphertext) {
ciphertext = coerceArray(ciphertext);
if ((ciphertext.length % 16) !== 0) {
throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
}
var plaintext = createArray(ciphertext.length);
var block = createArray(16);
for (var i = 0; i < ciphertext.length; i += 16) {
copyArray(ciphertext, block, 0, i, i + 16);
block = this._aes.decrypt(block);
for (var j = 0; j < 16; j++) {
plaintext[i + j] = block[j] ^ this._lastCipherblock[j];
}
copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16);
}
return plaintext;
}
/**
* Mode Of Operation - Cipher Feedback (CFB)
*/
var ModeOfOperationCFB = function(key, iv, segmentSize) {
if (!(this instanceof ModeOfOperationCFB)) {
throw Error('AES must be instanitated with `new`');
}
this.description = "Cipher Feedback";
this.name = "cfb";
if (!iv) {
iv = createArray(16);
} else if (iv.length != 16) {
throw new Error('invalid initialation vector size (must be 16 size)');
}
if (!segmentSize) { segmentSize = 1; }
this.segmentSize = segmentSize;
this._shiftRegister = coerceArray(iv, true);
this._aes = new AES(key);
}
ModeOfOperationCFB.prototype.encrypt = function(plaintext) {
if ((plaintext.length % this.segmentSize) != 0) {
throw new Error('invalid plaintext size (must be segmentSize bytes)');
}
var encrypted = coerceArray(plaintext, true);
var xorSegment;
for (var i = 0; i < encrypted.length; i += this.segmentSize) {
xorSegment = this._aes.encrypt(this._shiftRegister);
for (var j = 0; j < this.segmentSize; j++) {
encrypted[i + j] ^= xorSegment[j];
}
// Shift the register
copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
}
return encrypted;
}
ModeOfOperationCFB.prototype.decrypt = function(ciphertext) {
if ((ciphertext.length % this.segmentSize) != 0) {
throw new Error('invalid ciphertext size (must be segmentSize bytes)');
}
var plaintext = coerceArray(ciphertext, true);
var xorSegment;
for (var i = 0; i < plaintext.length; i += this.segmentSize) {
xorSegment = this._aes.encrypt(this._shiftRegister);
for (var j = 0; j < this.segmentSize; j++) {
plaintext[i + j] ^= xorSegment[j];
}
// Shift the register
copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
}
return plaintext;
}
/**
* Mode Of Operation - Output Feedback (OFB)
*/
var ModeOfOperationOFB = function(key, iv) {
if (!(this instanceof ModeOfOperationOFB)) {
throw Error('AES must be instanitated with `new`');
}
this.description = "Output Feedback";
this.name = "ofb";
if (!iv) {
iv = createArray(16);
} else if (iv.length != 16) {
throw new Error('invalid initialation vector size (must be 16 bytes)');
}
this._lastPrecipher = coerceArray(iv, true);
this._lastPrecipherIndex = 16;
this._aes = new AES(key);
}
ModeOfOperationOFB.prototype.encrypt = function(plaintext) {
var encrypted = coerceArray(plaintext, true);
for (var i = 0; i < encrypted.length; i++) {
if (this._lastPrecipherIndex === 16) {
this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
this._lastPrecipherIndex = 0;
}
encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
}
return encrypted;
}
// Decryption is symetric
ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;
/**
* Counter object for CTR common mode of operation
*/
var Counter = function(initialValue) {
if (!(this instanceof Counter)) {
throw Error('Counter must be instanitated with `new`');
}
// We allow 0, but anything false-ish uses the default 1
if (initialValue !== 0 && !initialValue) { initialValue = 1; }
if (typeof(initialValue) === 'number') {
this._counter = createArray(16);
this.setValue(initialValue);
} else {
this.setBytes(initialValue);
}
}
Counter.prototype.setValue = function(value) {
if (typeof(value) !== 'number' || parseInt(value) != value) {
throw new Error('invalid counter value (must be an integer)');
}
// We cannot safely handle numbers beyond the safe range for integers
if (value > Number.MAX_SAFE_INTEGER) {
throw new Error('integer value out of safe range');
}
for (var index = 15; index >= 0; --index) {
this._counter[index] = value % 256;
value = parseInt(value / 256);
}
}
Counter.prototype.setBytes = function(bytes) {
bytes = coerceArray(bytes, true);
if (bytes.length != 16) {
throw new Error('invalid counter bytes size (must be 16 bytes)');
}
this._counter = bytes;
};
Counter.prototype.increment = function() {
for (var i = 15; i >= 0; i--) {
if (this._counter[i] === 255) {
this._counter[i] = 0;
} else {
this._counter[i]++;
break;
}
}
}
/**
* Mode Of Operation - Counter (CTR)
*/
var ModeOfOperationCTR = function(key, counter) {
if (!(this instanceof ModeOfOperationCTR)) {
throw Error('AES must be instanitated with `new`');
}
this.description = "Counter";
this.name = "ctr";
if (!(counter instanceof Counter)) {
counter = new Counter(counter)
}
this._counter = counter;
this._remainingCounter = null;
this._remainingCounterIndex = 16;
this._aes = new AES(key);
}
ModeOfOperationCTR.prototype.encrypt = function(plaintext) {
var encrypted = coerceArray(plaintext, true);
for (var i = 0; i < encrypted.length; i++) {
if (this._remainingCounterIndex === 16) {
this._remainingCounter = this._aes.encrypt(this._counter._counter);
this._remainingCounterIndex = 0;
this._counter.increment();
}
encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
}
return encrypted;
}
// Decryption is symetric
ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt;
///////////////////////
// Padding
// See:https://tools.ietf.org/html/rfc2315
function pkcs7pad(data) {
data = coerceArray(data, true);
var padder = 16 - (data.length % 16);
var result = createArray(data.length + padder);
copyArray(data, result);
for (var i = data.length; i < result.length; i++) {
result[i] = padder;
}
return result;
}
function pkcs7strip(data) {
data = coerceArray(data, true);
if (data.length < 16) { throw new Error('PKCS#7 invalid length'); }
var padder = data[data.length - 1];
if (padder > 16) { throw new Error('PKCS#7 padding byte out of range'); }
var length = data.length - padder;
for (var i = 0; i < padder; i++) {
if (data[length + i] !== padder) {
throw new Error('PKCS#7 invalid padding byte');
}
}
var result = createArray(length);
copyArray(data, result, 0, 0, length);
return result;
}
///////////////////////
// Exporting
// The block cipher
var aesjs = {
AES: AES,
Counter: Counter,
ModeOfOperation: {
ecb: ModeOfOperationECB,
cbc: ModeOfOperationCBC,
cfb: ModeOfOperationCFB,
ofb: ModeOfOperationOFB,
ctr: ModeOfOperationCTR
},
utils: {
hex: convertHex,
utf8: convertUtf8
},
padding: {
pkcs7: {
pad: pkcs7pad,
strip: pkcs7strip
}
},
_arrayTest: {
coerceArray: coerceArray,
createArray: createArray,
copyArray: copyArray,
}
};
// node.js
if (typeof exports !== 'undefined') {
module.exports = aesjs
// RequireJS/AMD
// http://www.requirejs.org/docs/api.html
// https://github.com/amdjs/amdjs-api/wiki/AMD
} else if (typeof(define) === 'function' && define.amd) {
define(aesjs);
// Web Browsers
} else {
// If there was an existing library at "aesjs" make sure it's still available
if (root.aesjs) {
aesjs._aesjs = root.aesjs;
}
root.aesjs = aesjs;
}
})(this);

File diff suppressed because it is too large Load Diff

View File

@ -1,977 +0,0 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.MemoryMap = factory());
}(this, (function () { 'use strict';
/**
* Parser/writer for the "Intel hex" format.
*/
/*
* A regexp that matches lines in a .hex file.
*
* One hexadecimal character is matched by "[0-9A-Fa-f]".
* Two hex characters are matched by "[0-9A-Fa-f]{2}"
* Eight or more hex characters are matched by "[0-9A-Fa-f]{8,}"
* A capture group of two hex characters is "([0-9A-Fa-f]{2})"
*
* Record mark :
* 8 or more hex chars ([0-9A-Fa-f]{8,})
* Checksum ([0-9A-Fa-f]{2})
* Optional newline (?:\r\n|\r|\n|)
*/
var hexLineRegexp = /:([0-9A-Fa-f]{8,})([0-9A-Fa-f]{2})(?:\r\n|\r|\n|)/g;
// Takes a Uint8Array as input,
// Returns an integer in the 0-255 range.
function checksum(bytes) {
return (-bytes.reduce(function (sum, v){ return sum + v; }, 0)) & 0xFF;
}
// Takes two Uint8Arrays as input,
// Returns an integer in the 0-255 range.
function checksumTwo(array1, array2) {
var partial1 = array1.reduce(function (sum, v){ return sum + v; }, 0);
var partial2 = array2.reduce(function (sum, v){ return sum + v; }, 0);
return -( partial1 + partial2 ) & 0xFF;
}
// Trivial utility. Converts a number to hex and pads with zeroes up to 2 characters.
function hexpad(number) {
return number.toString(16).toUpperCase().padStart(2, '0');
}
// Polyfill as per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
Number.isInteger = Number.isInteger || function(value) {
return typeof value === 'number' &&
isFinite(value) &&
Math.floor(value) === value;
};
/**
* @class MemoryMap
*
* Represents the contents of a memory layout, with main focus into (possibly sparse) blocks of data.
*<br/>
* A {@linkcode MemoryMap} acts as a subclass of
* {@linkcode https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map|Map}.
* In every entry of it, the key is the starting address of a data block (an integer number),
* and the value is the <tt>Uint8Array</tt> with the data for that block.
*<br/>
* The main rationale for this is that a .hex file can contain a single block of contiguous
* data starting at memory address 0 (and it's the common case for simple .hex files),
* but complex files with several non-contiguous data blocks are also possible, thus
* the need for a data structure on top of the <tt>Uint8Array</tt>s.
*<br/>
* In order to parse <tt>.hex</tt> files, use the {@linkcode MemoryMap.fromHex} <em>static</em> factory
* method. In order to write <tt>.hex</tt> files, create a new {@linkcode MemoryMap} and call
* its {@linkcode MemoryMap.asHexString} method.
*
* @extends Map
* @example
* import MemoryMap from 'nrf-intel-hex';
*
* let memMap1 = new MemoryMap();
* let memMap2 = new MemoryMap([[0, new Uint8Array(1,2,3,4)]]);
* let memMap3 = new MemoryMap({0: new Uint8Array(1,2,3,4)});
* let memMap4 = new MemoryMap({0xCF0: new Uint8Array(1,2,3,4)});
*/
var MemoryMap = function MemoryMap(blocks) {
var this$1 = this;
this._blocks = new Map();
if (blocks && typeof blocks[Symbol.iterator] === 'function') {
for (var tuple of blocks) {
if (!(tuple instanceof Array) || tuple.length !== 2) {
throw new Error('First parameter to MemoryMap constructor must be an iterable of [addr, bytes] or undefined');
}
this$1.set(tuple[0], tuple[1]);
}
} else if (typeof blocks === 'object') {
// Try iterating through the object's keys
var addrs = Object.keys(blocks);
for (var addr of addrs) {
this$1.set(parseInt(addr), blocks[addr]);
}
} else if (blocks !== undefined && blocks !== null) {
throw new Error('First parameter to MemoryMap constructor must be an iterable of [addr, bytes] or undefined');
}
};
var prototypeAccessors = { size: { configurable: true } };
MemoryMap.prototype.set = function set (addr, value) {
if (!Number.isInteger(addr)) {
throw new Error('Address passed to MemoryMap is not an integer');
}
if (addr < 0) {
throw new Error('Address passed to MemoryMap is negative');
}
if (!(value instanceof Uint8Array)) {
throw new Error('Bytes passed to MemoryMap are not an Uint8Array');
}
return this._blocks.set(addr, value);
};
// Delegate the following to the 'this._blocks' Map:
MemoryMap.prototype.get = function get (addr){ return this._blocks.get(addr);};
MemoryMap.prototype.clear = function clear () { return this._blocks.clear(); };
MemoryMap.prototype.delete = function delete$1 (addr) { return this._blocks.delete(addr); };
MemoryMap.prototype.entries = function entries (){ return this._blocks.entries();};
MemoryMap.prototype.forEach = function forEach (callback, that) { return this._blocks.forEach(callback, that); };
MemoryMap.prototype.has = function has (addr){ return this._blocks.has(addr);};
MemoryMap.prototype.keys = function keys () { return this._blocks.keys(); };
MemoryMap.prototype.values = function values () { return this._blocks.values(); };
prototypeAccessors.size.get = function () { return this._blocks.size; };
MemoryMap.prototype[Symbol.iterator] = function () { return this._blocks[Symbol.iterator](); };
/**
* Parses a string containing data formatted in "Intel HEX" format, and
* returns an instance of {@linkcode MemoryMap}.
*<br/>
* The insertion order of keys in the {@linkcode MemoryMap} is guaranteed to be strictly
* ascending. In other words, when iterating through the {@linkcode MemoryMap}, the addresses
* will be ordered in ascending order.
*<br/>
* The parser has an opinionated behaviour, and will throw a descriptive error if it
* encounters some malformed input. Check the project's
* {@link https://github.com/NordicSemiconductor/nrf-intel-hex#Features|README file} for details.
*<br/>
* If <tt>maxBlockSize</tt> is given, any contiguous data block larger than that will
* be split in several blocks.
*
* @param {String} hexText The contents of a .hex file.
* @param {Number} [maxBlockSize=Infinity] Maximum size of the returned <tt>Uint8Array</tt>s.
*
* @return {MemoryMap}
*
* @example
* import MemoryMap from 'nrf-intel-hex';
*
* let intelHexString =
* ":100000000102030405060708090A0B0C0D0E0F1068\n" +
* ":00000001FF";
*
* let memMap = MemoryMap.fromHex(intelHexString);
*
* for (let [address, dataBlock] of memMap) {
* console.log('Data block at ', address, ', bytes: ', dataBlock);
* }
*/
MemoryMap.fromHex = function fromHex (hexText, maxBlockSize) {
if ( maxBlockSize === void 0 ) maxBlockSize = Infinity;
var blocks = new MemoryMap();
var lastCharacterParsed = 0;
var matchResult;
var recordCount = 0;
// Upper Linear Base Address, the 16 most significant bits (2 bytes) of
// the current 32-bit (4-byte) address
// In practice this is a offset that is summed to the "load offset" of the
// data records
var ulba = 0;
hexLineRegexp.lastIndex = 0; // Reset the regexp, if not it would skip content when called twice
while ((matchResult = hexLineRegexp.exec(hexText)) !== null) {
recordCount++;
// By default, a regexp loop ignores gaps between matches, but
// we want to be aware of them.
if (lastCharacterParsed !== matchResult.index) {
throw new Error(
'Malformed hex file: Could not parse between characters ' +
lastCharacterParsed +
' and ' +
matchResult.index +
' ("' +
hexText.substring(lastCharacterParsed, Math.min(matchResult.index, lastCharacterParsed + 16)).trim() +
'")');
}
lastCharacterParsed = hexLineRegexp.lastIndex;
// Give pretty names to the match's capture groups
var recordStr = matchResult[1];
var recordChecksum = matchResult[2];
// String to Uint8Array - https://stackoverflow.com/questions/43131242/how-to-convert-a-hexademical-string-of-data-to-an-arraybuffer-in-javascript
var recordBytes = new Uint8Array(recordStr.match(/[\da-f]{2}/gi).map(function (h){ return parseInt(h, 16); }));
var recordLength = recordBytes[0];
if (recordLength + 4 !== recordBytes.length) {
throw new Error('Mismatched record length at record ' + recordCount + ' (' + matchResult[0].trim() + '), expected ' + (recordLength) + ' data bytes but actual length is ' + (recordBytes.length - 4));
}
var cs = checksum(recordBytes);
if (parseInt(recordChecksum, 16) !== cs) {
throw new Error('Checksum failed at record ' + recordCount + ' (' + matchResult[0].trim() + '), should be ' + cs.toString(16) );
}
var offset = (recordBytes[1] << 8) + recordBytes[2];
var recordType = recordBytes[3];
var data = recordBytes.subarray(4);
if (recordType === 0) {
// Data record, contains data
// Create a new block, at (upper linear base address + offset)
if (blocks.has(ulba + offset)) {
throw new Error('Duplicated data at record ' + recordCount + ' (' + matchResult[0].trim() + ')');
}
if (offset + data.length > 0x10000) {
throw new Error(
'Data at record ' +
recordCount +
' (' +
matchResult[0].trim() +
') wraps over 0xFFFF. This would trigger ambiguous behaviour. Please restructure your data so that for every record the data offset plus the data length do not exceed 0xFFFF.');
}
blocks.set( ulba + offset, data );
} else {
// All non-data records must have a data offset of zero
if (offset !== 0) {
throw new Error('Record ' + recordCount + ' (' + matchResult[0].trim() + ') must have 0000 as data offset.');
}
switch (recordType) {
case 1: // EOF
if (lastCharacterParsed !== hexText.length) {
// This record should be at the very end of the string
throw new Error('There is data after an EOF record at record ' + recordCount);
}
return blocks.join(maxBlockSize);
case 2: // Extended Segment Address Record
// Sets the 16 most significant bits of the 20-bit Segment Base
// Address for the subsequent data.
ulba = ((data[0] << 8) + data[1]) << 4;
break;
case 3: // Start Segment Address Record
// Do nothing. Record type 3 only applies to 16-bit Intel CPUs,
// where it should reset the program counter (CS+IP CPU registers)
break;
case 4: // Extended Linear Address Record
// Sets the 16 most significant (upper) bits of the 32-bit Linear Address
// for the subsequent data
ulba = ((data[0] << 8) + data[1]) << 16;
break;
case 5: // Start Linear Address Record
// Do nothing. Record type 5 only applies to 32-bit Intel CPUs,
// where it should reset the program counter (EIP CPU register)
// It might have meaning for other CPU architectures
// (see http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka9903.html )
// but will be ignored nonetheless.
break;
default:
throw new Error('Invalid record type 0x' + hexpad(recordType) + ' at record ' + recordCount + ' (should be between 0x00 and 0x05)');
}
}
}
if (recordCount) {
throw new Error('No EOF record at end of file');
} else {
throw new Error('Malformed .hex file, could not parse any registers');
}
};
/**
* Returns a <strong>new</strong> instance of {@linkcode MemoryMap}, containing
* the same data, but concatenating together those memory blocks that are adjacent.
*<br/>
* The insertion order of keys in the {@linkcode MemoryMap} is guaranteed to be strictly
* ascending. In other words, when iterating through the {@linkcode MemoryMap}, the addresses
* will be ordered in ascending order.
*<br/>
* If <tt>maxBlockSize</tt> is given, blocks will be concatenated together only
* until the joined block reaches this size in bytes. This means that the output
* {@linkcode MemoryMap} might have more entries than the input one.
*<br/>
* If there is any overlap between blocks, an error will be thrown.
*<br/>
* The returned {@linkcode MemoryMap} will use newly allocated memory.
*
* @param {Number} [maxBlockSize=Infinity] Maximum size of the <tt>Uint8Array</tt>s in the
* returned {@linkcode MemoryMap}.
*
* @return {MemoryMap}
*/
MemoryMap.prototype.join = function join (maxBlockSize) {
var this$1 = this;
if ( maxBlockSize === void 0 ) maxBlockSize = Infinity;
// First pass, create a Map of address→length of contiguous blocks
var sortedKeys = Array.from(this.keys()).sort(function (a,b){ return a-b; });
var blockSizes = new Map();
var lastBlockAddr = -1;
var lastBlockEndAddr = -1;
for (var i=0,l=sortedKeys.length; i<l; i++) {
var blockAddr = sortedKeys[i];
var blockLength = this$1.get(sortedKeys[i]).length;
if (lastBlockEndAddr === blockAddr && (lastBlockEndAddr - lastBlockAddr) < maxBlockSize) {
// Grow when the previous end address equals the current,
// and we don't go over the maximum block size.
blockSizes.set(lastBlockAddr, blockSizes.get(lastBlockAddr) + blockLength);
lastBlockEndAddr += blockLength;
} else if (lastBlockEndAddr <= blockAddr) {
// Else mark a new block.
blockSizes.set(blockAddr, blockLength);
lastBlockAddr = blockAddr;
lastBlockEndAddr = blockAddr + blockLength;
} else {
throw new Error('Overlapping data around address 0x' + blockAddr.toString(16));
}
}
// Second pass: allocate memory for the contiguous blocks and copy data around.
var mergedBlocks = new MemoryMap();
var mergingBlock;
var mergingBlockAddr = -1;
for (var i$1=0,l$1=sortedKeys.length; i$1<l$1; i$1++) {
var blockAddr$1 = sortedKeys[i$1];
if (blockSizes.has(blockAddr$1)) {
mergingBlock = new Uint8Array(blockSizes.get(blockAddr$1));
mergedBlocks.set(blockAddr$1, mergingBlock);
mergingBlockAddr = blockAddr$1;
}
mergingBlock.set(this$1.get(blockAddr$1), blockAddr$1 - mergingBlockAddr);
}
return mergedBlocks;
};
/**
* Given a {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map|<tt>Map</tt>}
* of {@linkcode MemoryMap}s, indexed by a alphanumeric ID,
* returns a <tt>Map</tt> of address to tuples (<tt>Arrays</tt>s of length 2) of the form
* <tt>(id, Uint8Array)</tt>s.
*<br/>
* The scenario for using this is having several {@linkcode MemoryMap}s, from several calls to
* {@link module:nrf-intel-hex~hexToArrays|hexToArrays}, each having a different identifier.
* This function locates where those memory block sets overlap, and returns a <tt>Map</tt>
* containing addresses as keys, and arrays as values. Each array will contain 1 or more
* <tt>(id, Uint8Array)</tt> tuples: the identifier of the memory block set that has
* data in that region, and the data itself. When memory block sets overlap, there will
* be more than one tuple.
*<br/>
* The <tt>Uint8Array</tt>s in the output are
* {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray|subarrays}
* of the input data; new memory is <strong>not</strong> allocated for them.
*<br/>
* The insertion order of keys in the output <tt>Map</tt> is guaranteed to be strictly
* ascending. In other words, when iterating through the <tt>Map</tt>, the addresses
* will be ordered in ascending order.
*<br/>
* When two blocks overlap, the corresponding array of tuples will have the tuples ordered
* in the insertion order of the input <tt>Map</tt> of block sets.
*<br/>
*
* @param {Map.MemoryMap} memoryMaps The input memory block sets
*
* @example
* import MemoryMap from 'nrf-intel-hex';
*
* let memMap1 = MemoryMap.fromHex( hexdata1 );
* let memMap2 = MemoryMap.fromHex( hexdata2 );
* let memMap3 = MemoryMap.fromHex( hexdata3 );
*
* let maps = new Map([
* ['file A', blocks1],
* ['file B', blocks2],
* ['file C', blocks3]
* ]);
*
* let overlappings = MemoryMap.overlapMemoryMaps(maps);
*
* for (let [address, tuples] of overlappings) {
* // if 'tuples' has length > 1, there is an overlap starting at 'address'
*
* for (let [address, tuples] of overlappings) {
* let [id, bytes] = tuple;
* // 'id' in this example is either 'file A', 'file B' or 'file C'
* }
* }
* @return {Map.Array<mixed,Uint8Array>} The map of possibly overlapping memory blocks
*/
MemoryMap.overlapMemoryMaps = function overlapMemoryMaps (memoryMaps) {
// First pass: create a list of addresses where any block starts or ends.
var cuts = new Set();
for (var [, blocks] of memoryMaps) {
for (var [address, block] of blocks) {
cuts.add(address);
cuts.add(address + block.length);
}
}
var orderedCuts = Array.from(cuts.values()).sort(function (a,b){ return a-b; });
var overlaps = new Map();
// Second pass: iterate through the cuts, get slices of every intersecting blockset
var loop = function ( i, l ) {
var cut = orderedCuts[i];
var nextCut = orderedCuts[i+1];
var tuples = [];
for (var [setId, blocks$1] of memoryMaps) {
// Find the block with the highest address that is equal or lower to
// the current cut (if any)
var blockAddr = Array.from(blocks$1.keys()).reduce(function (acc, val){
if (val > cut) {
return acc;
}
return Math.max( acc, val );
}, -1);
if (blockAddr !== -1) {
var block$1 = blocks$1.get(blockAddr);
var subBlockStart = cut - blockAddr;
var subBlockEnd = nextCut - blockAddr;
if (subBlockStart < block$1.length) {
tuples.push([ setId, block$1.subarray(subBlockStart, subBlockEnd) ]);
}
}
}
if (tuples.length) {
overlaps.set(cut, tuples);
}
};
for (var i=0, l=orderedCuts.length-1; i<l; i++) loop( i, l );
return overlaps;
};
/**
* Given the output of the {@linkcode MemoryMap.overlapMemoryMaps|overlapMemoryMaps}
* (a <tt>Map</tt> of address to an <tt>Array</tt> of <tt>(id, Uint8Array)</tt> tuples),
* returns a {@linkcode MemoryMap}. This discards the IDs in the process.
*<br/>
* The output <tt>Map</tt> contains as many entries as the input one (using the same addresses
* as keys), but the value for each entry will be the <tt>Uint8Array</tt> of the <b>last</b>
* tuple for each address in the input data.
*<br/>
* The scenario is wanting to join together several parsed .hex files, not worrying about
* their overlaps.
*<br/>
*
* @param {Map.Array<mixed,Uint8Array>} overlaps The (possibly overlapping) input memory blocks
* @return {MemoryMap} The flattened memory blocks
*/
MemoryMap.flattenOverlaps = function flattenOverlaps (overlaps) {
return new MemoryMap(
Array.from(overlaps.entries()).map(function (ref) {
var address = ref[0];
var tuples = ref[1];
return [address, tuples[tuples.length - 1][1] ];
})
);
};
/**
* Returns a new instance of {@linkcode MemoryMap}, where:
*
* <ul>
* <li>Each key (the start address of each <tt>Uint8Array</tt>) is a multiple of
*<tt>pageSize</tt></li>
* <li>The size of each <tt>Uint8Array</tt> is exactly <tt>pageSize</tt></li>
* <li>Bytes from the input map to bytes in the output</li>
* <li>Bytes not in the input are replaced by a padding value</li>
* </ul>
*<br/>
* The scenario is wanting to prepare pages of bytes for a write operation, where the write
* operation affects a whole page/sector at once.
*<br/>
* The insertion order of keys in the output {@linkcode MemoryMap} is guaranteed
* to be strictly ascending. In other words, when iterating through the
* {@linkcode MemoryMap}, the addresses will be ordered in ascending order.
*<br/>
* The <tt>Uint8Array</tt>s in the output will be newly allocated.
*<br/>
*
* @param {Number} [pageSize=1024] The size of the output pages, in bytes
* @param {Number} [pad=0xFF] The byte value to use for padding
* @return {MemoryMap}
*/
MemoryMap.prototype.paginate = function paginate ( pageSize, pad) {
var this$1 = this;
if ( pageSize === void 0 ) pageSize=1024;
if ( pad === void 0 ) pad=0xFF;
if (pageSize <= 0) {
throw new Error('Page size must be greater than zero');
}
var outPages = new MemoryMap();
var page;
var sortedKeys = Array.from(this.keys()).sort(function (a,b){ return a-b; });
for (var i=0,l=sortedKeys.length; i<l; i++) {
var blockAddr = sortedKeys[i];
var block = this$1.get(blockAddr);
var blockLength = block.length;
var blockEnd = blockAddr + blockLength;
for (var pageAddr = blockAddr - (blockAddr % pageSize); pageAddr < blockEnd; pageAddr += pageSize) {
page = outPages.get(pageAddr);
if (!page) {
page = new Uint8Array(pageSize);
page.fill(pad);
outPages.set(pageAddr, page);
}
var offset = pageAddr - blockAddr;
var subBlock = (void 0);
if (offset <= 0) {
// First page which intersects the block
subBlock = block.subarray(0, Math.min(pageSize + offset, blockLength));
page.set(subBlock, -offset);
} else {
// Any other page which intersects the block
subBlock = block.subarray(offset, offset + Math.min(pageSize, blockLength - offset));
page.set(subBlock, 0);
}
}
}
return outPages;
};
/**
* Locates the <tt>Uint8Array</tt> which contains the given offset,
* and returns the four bytes held at that offset, as a 32-bit unsigned integer.
*
*<br/>
* Behaviour is similar to {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32|DataView.prototype.getUint32},
* except that this operates over a {@linkcode MemoryMap} instead of
* over an <tt>ArrayBuffer</tt>, and that this may return <tt>undefined</tt> if
* the address is not <em>entirely</em> contained within one of the <tt>Uint8Array</tt>s.
*<br/>
*
* @param {Number} offset The memory offset to read the data
* @param {Boolean} [littleEndian=false] Whether to fetch the 4 bytes as a little- or big-endian integer
* @return {Number|undefined} An unsigned 32-bit integer number
*/
MemoryMap.prototype.getUint32 = function getUint32 (offset, littleEndian) {
var this$1 = this;
var keys = Array.from(this.keys());
for (var i=0,l=keys.length; i<l; i++) {
var blockAddr = keys[i];
var block = this$1.get(blockAddr);
var blockLength = block.length;
var blockEnd = blockAddr + blockLength;
if (blockAddr <= offset && (offset+4) <= blockEnd) {
return (new DataView(block.buffer, offset - blockAddr, 4)).getUint32(0, littleEndian);
}
}
return;
};
/**
* Returns a <tt>String</tt> of text representing a .hex file.
* <br/>
* The writer has an opinionated behaviour. Check the project's
* {@link https://github.com/NordicSemiconductor/nrf-intel-hex#Features|README file} for details.
*
* @param {Number} [lineSize=16] Maximum number of bytes to be encoded in each data record.
* Must have a value between 1 and 255, as per the specification.
*
* @return {String} String of text with the .hex representation of the input binary data
*
* @example
* import MemoryMap from 'nrf-intel-hex';
*
* let memMap = new MemoryMap();
* let bytes = new Uint8Array(....);
* memMap.set(0x0FF80000, bytes); // The block with 'bytes' will start at offset 0x0FF80000
*
* let string = memMap.asHexString();
*/
MemoryMap.prototype.asHexString = function asHexString (lineSize) {
var this$1 = this;
if ( lineSize === void 0 ) lineSize = 16;
var lowAddress = 0;// 16 least significant bits of the current addr
var highAddress = -1 << 16; // 16 most significant bits of the current addr
var records = [];
if (lineSize <=0) {
throw new Error('Size of record must be greater than zero');
} else if (lineSize > 255) {
throw new Error('Size of record must be less than 256');
}
// Placeholders
var offsetRecord = new Uint8Array(6);
var recordHeader = new Uint8Array(4);
var sortedKeys = Array.from(this.keys()).sort(function (a,b){ return a-b; });
for (var i=0,l=sortedKeys.length; i<l; i++) {
var blockAddr = sortedKeys[i];
var block = this$1.get(blockAddr);
// Sanity checks
if (!(block instanceof Uint8Array)) {
throw new Error('Block at offset ' + blockAddr + ' is not an Uint8Array');
}
if (blockAddr < 0) {
throw new Error('Block at offset ' + blockAddr + ' has a negative thus invalid address');
}
var blockSize = block.length;
if (!blockSize) { continue; } // Skip zero-length blocks
if (blockAddr > (highAddress + 0xFFFF)) {
// Insert a new 0x04 record to jump to a new 64KiB block
// Round up the least significant 16 bits - no bitmasks because they trigger
// base-2 negative numbers, whereas subtracting the modulo maintains precision
highAddress = blockAddr - blockAddr % 0x10000;
lowAddress = 0;
offsetRecord[0] = 2;// Length
offsetRecord[1] = 0;// Load offset, high byte
offsetRecord[2] = 0;// Load offset, low byte
offsetRecord[3] = 4;// Record type
offsetRecord[4] = highAddress >> 24;// new address offset, high byte
offsetRecord[5] = highAddress >> 16;// new address offset, low byte
records.push(
':' +
Array.prototype.map.call(offsetRecord, hexpad).join('') +
hexpad(checksum(offsetRecord))
);
}
if (blockAddr < (highAddress + lowAddress)) {
throw new Error(
'Block starting at 0x' +
blockAddr.toString(16) +
' overlaps with a previous block.');
}
lowAddress = blockAddr % 0x10000;
var blockOffset = 0;
var blockEnd = blockAddr + blockSize;
if (blockEnd > 0xFFFFFFFF) {
throw new Error('Data cannot be over 0xFFFFFFFF');
}
// Loop for every 64KiB memory segment that spans this block
while (highAddress + lowAddress < blockEnd) {
if (lowAddress > 0xFFFF) {
// Insert a new 0x04 record to jump to a new 64KiB block
highAddress += 1 << 16; // Increase by one
lowAddress = 0;
offsetRecord[0] = 2;// Length
offsetRecord[1] = 0;// Load offset, high byte
offsetRecord[2] = 0;// Load offset, low byte
offsetRecord[3] = 4;// Record type
offsetRecord[4] = highAddress >> 24;// new address offset, high byte
offsetRecord[5] = highAddress >> 16;// new address offset, low byte
records.push(
':' +
Array.prototype.map.call(offsetRecord, hexpad).join('') +
hexpad(checksum(offsetRecord))
);
}
var recordSize = -1;
// Loop for every record for that spans the current 64KiB memory segment
while (lowAddress < 0x10000 && recordSize) {
recordSize = Math.min(
lineSize, // Normal case
blockEnd - highAddress - lowAddress, // End of block
0x10000 - lowAddress // End of low addresses
);
if (recordSize) {
recordHeader[0] = recordSize; // Length
recordHeader[1] = lowAddress >> 8;// Load offset, high byte
recordHeader[2] = lowAddress;// Load offset, low byte
recordHeader[3] = 0;// Record type
var subBlock = block.subarray(blockOffset, blockOffset + recordSize); // Data bytes for this record
records.push(
':' +
Array.prototype.map.call(recordHeader, hexpad).join('') +
Array.prototype.map.call(subBlock, hexpad).join('') +
hexpad(checksumTwo(recordHeader, subBlock))
);
blockOffset += recordSize;
lowAddress += recordSize;
}
}
}
}
records.push(':00000001FF');// EOF record
return records.join('\n');
};
/**
* Performs a deep copy of the current {@linkcode MemoryMap}, returning a new one
* with exactly the same contents, but allocating new memory for each of its
* <tt>Uint8Array</tt>s.
*
* @return {MemoryMap}
*/
MemoryMap.prototype.clone = function clone () {
var this$1 = this;
var cloned = new MemoryMap();
for (var [addr, value] of this$1) {
cloned.set(addr, new Uint8Array(value));
}
return cloned;
};
/**
* Given one <tt>Uint8Array</tt>, looks through its contents and returns a new
* {@linkcode MemoryMap}, stripping away those regions where there are only
* padding bytes.
* <br/>
* The start of the input <tt>Uint8Array</tt> is assumed to be offset zero for the output.
* <br/>
* The use case here is dumping memory from a working device and try to see the
* "interesting" memory regions it has. This assumes that there is a constant,
* predefined padding byte value being used in the "non-interesting" regions.
* In other words: this will work as long as the dump comes from a flash memory
* which has been previously erased (thus <tt>0xFF</tt>s for padding), or from a
* previously blanked HDD (thus <tt>0x00</tt>s for padding).
* <br/>
* This method uses <tt>subarray</tt> on the input data, and thus does not allocate memory
* for the <tt>Uint8Array</tt>s.
*
* @param {Uint8Array} bytes The input data
* @param {Number} [padByte=0xFF] The value of the byte assumed to be used as padding
* @param {Number} [minPadLength=64] The minimum number of consecutive pad bytes to
* be considered actual padding
*
* @return {MemoryMap}
*/
MemoryMap.fromPaddedUint8Array = function fromPaddedUint8Array (bytes, padByte, minPadLength) {
if ( padByte === void 0 ) padByte=0xFF;
if ( minPadLength === void 0 ) minPadLength=64;
if (!(bytes instanceof Uint8Array)) {
throw new Error('Bytes passed to fromPaddedUint8Array are not an Uint8Array');
}
// The algorithm used is naïve and checks every byte.
// An obvious optimization would be to implement Boyer-Moore
// (see https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm )
// or otherwise start skipping up to minPadLength bytes when going through a non-pad
// byte.
// Anyway, we could expect a lot of cases where there is a majority of pad bytes,
// and the algorithm should check most of them anyway, so the perf gain is questionable.
var memMap = new MemoryMap();
var consecutivePads = 0;
var lastNonPad = -1;
var firstNonPad = 0;
var skippingBytes = false;
var l = bytes.length;
for (var addr = 0; addr < l; addr++) {
var byte = bytes[addr];
if (byte === padByte) {
consecutivePads++;
if (consecutivePads >= minPadLength) {
// Edge case: ignore writing a zero-length block when skipping
// bytes at the beginning of the input
if (lastNonPad !== -1) {
/// Add the previous block to the result memMap
memMap.set(firstNonPad, bytes.subarray(firstNonPad, lastNonPad+1));
}
skippingBytes = true;
}
} else {
if (skippingBytes) {
skippingBytes = false;
firstNonPad = addr;
}
lastNonPad = addr;
consecutivePads = 0;
}
}
// At EOF, add the last block if not skipping bytes already (and input not empty)
if (!skippingBytes && lastNonPad !== -1) {
memMap.set(firstNonPad, bytes.subarray(firstNonPad, l));
}
return memMap;
};
/**
* Returns a new instance of {@linkcode MemoryMap}, containing only data between
* the addresses <tt>address</tt> and <tt>address + length</tt>.
* Behaviour is similar to {@linkcode https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/slice|Array.prototype.slice},
* in that the return value is a portion of the current {@linkcode MemoryMap}.
*
* <br/>
* The returned {@linkcode MemoryMap} might be empty.
*
* <br/>
* Internally, this uses <tt>subarray</tt>, so new memory is not allocated.
*
* @param {Number} address The start address of the slice
* @param {Number} length The length of memory map to slice out
* @return {MemoryMap}
*/
MemoryMap.prototype.slice = function slice (address, length){
var this$1 = this;
if ( length === void 0 ) length = Infinity;
if (length < 0) {
throw new Error('Length of the slice cannot be negative');
}
var sliced = new MemoryMap();
for (var [blockAddr, block] of this$1) {
var blockLength = block.length;
if ((blockAddr + blockLength) >= address && blockAddr < (address + length)) {
var sliceStart = Math.max(address, blockAddr);
var sliceEnd = Math.min(address + length, blockAddr + blockLength);
var sliceLength = sliceEnd - sliceStart;
var relativeSliceStart = sliceStart - blockAddr;
if (sliceLength > 0) {
sliced.set(sliceStart, block.subarray(relativeSliceStart, relativeSliceStart + sliceLength));
}
}
}
return sliced;
};
/**
* Returns a new instance of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32|Uint8Array}, containing only data between
* the addresses <tt>address</tt> and <tt>address + length</tt>. Any byte without a value
* in the input {@linkcode MemoryMap} will have a value of <tt>padByte</tt>.
*
* <br/>
* This method allocates new memory.
*
* @param {Number} address The start address of the slice
* @param {Number} length The length of memory map to slice out
* @param {Number} [padByte=0xFF] The value of the byte assumed to be used as padding
* @return {MemoryMap}
*/
MemoryMap.prototype.slicePad = function slicePad (address, length, padByte){
var this$1 = this;
if ( padByte === void 0 ) padByte=0xFF;
if (length < 0) {
throw new Error('Length of the slice cannot be negative');
}
var out = (new Uint8Array(length)).fill(padByte);
for (var [blockAddr, block] of this$1) {
var blockLength = block.length;
if ((blockAddr + blockLength) >= address && blockAddr < (address + length)) {
var sliceStart = Math.max(address, blockAddr);
var sliceEnd = Math.min(address + length, blockAddr + blockLength);
var sliceLength = sliceEnd - sliceStart;
var relativeSliceStart = sliceStart - blockAddr;
if (sliceLength > 0) {
out.set(block.subarray(relativeSliceStart, relativeSliceStart + sliceLength), sliceStart - address);
}
}
}
return out;
};
/**
* Checks whether the current memory map contains the one given as a parameter.
*
* <br/>
* "Contains" means that all the offsets that have a byte value in the given
* memory map have a value in the current memory map, and that the byte values
* are the same.
*
* <br/>
* An empty memory map is always contained in any other memory map.
*
* <br/>
* Returns boolean <tt>true</tt> if the memory map is contained, <tt>false</tt>
* otherwise.
*
* @param {MemoryMap} memMap The memory map to check
* @return {Boolean}
*/
MemoryMap.prototype.contains = function contains (memMap) {
var this$1 = this;
for (var [blockAddr, block] of memMap) {
var blockLength = block.length;
var slice = this$1.slice(blockAddr, blockLength).join().get(blockAddr);
if ((!slice) || slice.length !== blockLength ) {
return false;
}
for (var i in block) {
if (block[i] !== slice[i]) {
return false;
}
}
}
return true;
};
Object.defineProperties( MemoryMap.prototype, prototypeAccessors );
return MemoryMap;
})));
//# sourceMappingURL=intel-hex.browser.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,753 +0,0 @@
//Copyright 2014-2015 Google Inc. All rights reserved.
//Use of this source code is governed by a BSD-style
//license that can be found in the LICENSE file or at
//https://developers.google.com/open-source/licenses/bsd
/**
* @fileoverview The U2F api.
*/
'use strict';
/**
* Namespace for the U2F api.
* @type {Object}
*/
var u2f = u2f || {};
/**
* FIDO U2F Javascript API Version
* @number
*/
var js_api_version;
/**
* The U2F extension id
* @const {string}
*/
// The Chrome packaged app extension ID.
// Uncomment this if you want to deploy a server instance that uses
// the package Chrome app and does not require installing the U2F Chrome extension.
u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd';
// The U2F Chrome extension ID.
// Uncomment this if you want to deploy a server instance that uses
// the U2F Chrome extension to authenticate.
// u2f.EXTENSION_ID = 'pfboblefjcgdjicmnffhdgionmgcdmne';
/**
* Message types for messsages to/from the extension
* @const
* @enum {string}
*/
u2f.MessageTypes = {
'U2F_REGISTER_REQUEST': 'u2f_register_request',
'U2F_REGISTER_RESPONSE': 'u2f_register_response',
'U2F_SIGN_REQUEST': 'u2f_sign_request',
'U2F_SIGN_RESPONSE': 'u2f_sign_response',
'U2F_GET_API_VERSION_REQUEST': 'u2f_get_api_version_request',
'U2F_GET_API_VERSION_RESPONSE': 'u2f_get_api_version_response'
};
/**
* Response status codes
* @const
* @enum {number}
*/
u2f.ErrorCodes = {
'OK': 0,
'OTHER_ERROR': 1,
'BAD_REQUEST': 2,
'CONFIGURATION_UNSUPPORTED': 3,
'DEVICE_INELIGIBLE': 4,
'TIMEOUT': 5
};
/**
* A message for registration requests
* @typedef {{
* type: u2f.MessageTypes,
* appId: ?string,
* timeoutSeconds: ?number,
* requestId: ?number
* }}
*/
u2f.U2fRequest;
/**
* A message for registration responses
* @typedef {{
* type: u2f.MessageTypes,
* responseData: (u2f.Error | u2f.RegisterResponse | u2f.SignResponse),
* requestId: ?number
* }}
*/
u2f.U2fResponse;
/**
* An error object for responses
* @typedef {{
* errorCode: u2f.ErrorCodes,
* errorMessage: ?string
* }}
*/
u2f.Error;
/**
* Data object for a single sign request.
* @typedef {enum {BLUETOOTH_RADIO, BLUETOOTH_LOW_ENERGY, USB, NFC}}
*/
u2f.Transport;
/**
* Data object for a single sign request.
* @typedef {Array<u2f.Transport>}
*/
u2f.Transports;
/**
* Data object for a single sign request.
* @typedef {{
* version: string,
* challenge: string,
* keyHandle: string,
* appId: string
* }}
*/
u2f.SignRequest;
/**
* Data object for a sign response.
* @typedef {{
* keyHandle: string,
* signatureData: string,
* clientData: string
* }}
*/
u2f.SignResponse;
/**
* Data object for a registration request.
* @typedef {{
* version: string,
* challenge: string
* }}
*/
u2f.RegisterRequest;
/**
* Data object for a registration response.
* @typedef {{
* version: string,
* keyHandle: string,
* transports: Transports,
* appId: string
* }}
*/
u2f.RegisterResponse;
/**
* Data object for a registered key.
* @typedef {{
* version: string,
* keyHandle: string,
* transports: ?Transports,
* appId: ?string
* }}
*/
u2f.RegisteredKey;
/**
* Data object for a get API register response.
* @typedef {{
* js_api_version: number
* }}
*/
u2f.GetJsApiVersionResponse;
//Low level MessagePort API support
/**
* Sets up a MessagePort to the U2F extension using the
* available mechanisms.
* @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
*/
u2f.getMessagePort = function(callback) {
if (typeof chrome != 'undefined' && chrome.runtime) {
// The actual message here does not matter, but we need to get a reply
// for the callback to run. Thus, send an empty signature request
// in order to get a failure response.
var msg = {
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
signRequests: []
};
chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() {
if (!chrome.runtime.lastError) {
// We are on a whitelisted origin and can talk directly
// with the extension.
u2f.getChromeRuntimePort_(callback);
} else {
// chrome.runtime was available, but we couldn't message
// the extension directly, use iframe
u2f.getIframePort_(callback);
}
});
} else if (u2f.isAndroidChrome_()) {
u2f.getAuthenticatorPort_(callback);
} else if (u2f.isIosChrome_()) {
u2f.getIosPort_(callback);
} else {
// chrome.runtime was not available at all, which is normal
// when this origin doesn't have access to any extensions.
u2f.getIframePort_(callback);
}
};
/**
* Detect chrome running on android based on the browser's useragent.
* @private
*/
u2f.isAndroidChrome_ = function() {
var userAgent = navigator.userAgent;
return userAgent.indexOf('Chrome') != -1 &&
userAgent.indexOf('Android') != -1;
};
/**
* Detect chrome running on iOS based on the browser's platform.
* @private
*/
u2f.isIosChrome_ = function() {
return ["iPhone", "iPad", "iPod"].indexOf(navigator.platform) > -1;
};
/**
* Connects directly to the extension via chrome.runtime.connect.
* @param {function(u2f.WrappedChromeRuntimePort_)} callback
* @private
*/
u2f.getChromeRuntimePort_ = function(callback) {
var port = chrome.runtime.connect(u2f.EXTENSION_ID,
{'includeTlsChannelId': true});
setTimeout(function() {
callback(new u2f.WrappedChromeRuntimePort_(port));
}, 0);
};
/**
* Return a 'port' abstraction to the Authenticator app.
* @param {function(u2f.WrappedAuthenticatorPort_)} callback
* @private
*/
u2f.getAuthenticatorPort_ = function(callback) {
setTimeout(function() {
callback(new u2f.WrappedAuthenticatorPort_());
}, 0);
};
/**
* Return a 'port' abstraction to the iOS client app.
* @param {function(u2f.WrappedIosPort_)} callback
* @private
*/
u2f.getIosPort_ = function(callback) {
setTimeout(function() {
callback(new u2f.WrappedIosPort_());
}, 0);
};
/**
* A wrapper for chrome.runtime.Port that is compatible with MessagePort.
* @param {Port} port
* @constructor
* @private
*/
u2f.WrappedChromeRuntimePort_ = function(port) {
this.port_ = port;
};
/**
* Format and return a sign request compliant with the JS API version supported by the extension.
* @param {Array<u2f.SignRequest>} signRequests
* @param {number} timeoutSeconds
* @param {number} reqId
* @return {Object}
*/
u2f.formatSignRequest_ =
function(appId, challenge, registeredKeys, timeoutSeconds, reqId) {
if (js_api_version === undefined || js_api_version < 1.1) {
// Adapt request to the 1.0 JS API
var signRequests = [];
for (var i = 0; i < registeredKeys.length; i++) {
signRequests[i] = {
version: registeredKeys[i].version,
challenge: challenge,
keyHandle: registeredKeys[i].keyHandle,
appId: appId
};
}
return {
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
signRequests: signRequests,
timeoutSeconds: timeoutSeconds,
requestId: reqId
};
}
// JS 1.1 API
return {
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
appId: appId,
challenge: challenge,
registeredKeys: registeredKeys,
timeoutSeconds: timeoutSeconds,
requestId: reqId
};
};
/**
* Format and return a register request compliant with the JS API version supported by the extension..
* @param {Array<u2f.SignRequest>} signRequests
* @param {Array<u2f.RegisterRequest>} signRequests
* @param {number} timeoutSeconds
* @param {number} reqId
* @return {Object}
*/
u2f.formatRegisterRequest_ =
function(appId, registeredKeys, registerRequests, timeoutSeconds, reqId) {
if (js_api_version === undefined || js_api_version < 1.1) {
// Adapt request to the 1.0 JS API
for (var i = 0; i < registerRequests.length; i++) {
registerRequests[i].appId = appId;
}
var signRequests = [];
for (var i = 0; i < registeredKeys.length; i++) {
signRequests[i] = {
version: registeredKeys[i].version,
challenge: registerRequests[0],
keyHandle: registeredKeys[i].keyHandle,
appId: appId
};
}
return {
type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
signRequests: signRequests,
registerRequests: registerRequests,
timeoutSeconds: timeoutSeconds,
requestId: reqId
};
}
// JS 1.1 API
return {
type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
appId: appId,
registerRequests: registerRequests,
registeredKeys: registeredKeys,
timeoutSeconds: timeoutSeconds,
requestId: reqId
};
};
/**
* Posts a message on the underlying channel.
* @param {Object} message
*/
u2f.WrappedChromeRuntimePort_.prototype.postMessage = function(message) {
this.port_.postMessage(message);
};
/**
* Emulates the HTML 5 addEventListener interface. Works only for the
* onmessage event, which is hooked up to the chrome.runtime.Port.onMessage.
* @param {string} eventName
* @param {function({data: Object})} handler
*/
u2f.WrappedChromeRuntimePort_.prototype.addEventListener =
function(eventName, handler) {
var name = eventName.toLowerCase();
if (name == 'message' || name == 'onmessage') {
this.port_.onMessage.addListener(function(message) {
// Emulate a minimal MessageEvent object
handler({'data': message});
});
} else {
console.error('WrappedChromeRuntimePort only supports onMessage');
}
};
/**
* Wrap the Authenticator app with a MessagePort interface.
* @constructor
* @private
*/
u2f.WrappedAuthenticatorPort_ = function() {
this.requestId_ = -1;
this.requestObject_ = null;
}
/**
* Launch the Authenticator intent.
* @param {Object} message
*/
u2f.WrappedAuthenticatorPort_.prototype.postMessage = function(message) {
var intentUrl =
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +
';S.request=' + encodeURIComponent(JSON.stringify(message)) +
';end';
document.location = intentUrl;
};
/**
* Tells what type of port this is.
* @return {String} port type
*/
u2f.WrappedAuthenticatorPort_.prototype.getPortType = function() {
return "WrappedAuthenticatorPort_";
};
/**
* Emulates the HTML 5 addEventListener interface.
* @param {string} eventName
* @param {function({data: Object})} handler
*/
u2f.WrappedAuthenticatorPort_.prototype.addEventListener = function(eventName, handler) {
var name = eventName.toLowerCase();
if (name == 'message') {
var self = this;
/* Register a callback to that executes when
* chrome injects the response. */
window.addEventListener(
'message', self.onRequestUpdate_.bind(self, handler), false);
} else {
console.error('WrappedAuthenticatorPort only supports message');
}
};
/**
* Callback invoked when a response is received from the Authenticator.
* @param function({data: Object}) callback
* @param {Object} message message Object
*/
u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ =
function(callback, message) {
var messageObject = JSON.parse(message.data);
var intentUrl = messageObject['intentURL'];
var errorCode = messageObject['errorCode'];
var responseObject = null;
if (messageObject.hasOwnProperty('data')) {
responseObject = /** @type {Object} */ (
JSON.parse(messageObject['data']));
}
callback({'data': responseObject});
};
/**
* Base URL for intents to Authenticator.
* @const
* @private
*/
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ =
'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE';
/**
* Wrap the iOS client app with a MessagePort interface.
* @constructor
* @private
*/
u2f.WrappedIosPort_ = function() {};
/**
* Launch the iOS client app request
* @param {Object} message
*/
u2f.WrappedIosPort_.prototype.postMessage = function(message) {
var str = JSON.stringify(message);
var url = "u2f://auth?" + encodeURI(str);
location.replace(url);
};
/**
* Tells what type of port this is.
* @return {String} port type
*/
u2f.WrappedIosPort_.prototype.getPortType = function() {
return "WrappedIosPort_";
};
/**
* Emulates the HTML 5 addEventListener interface.
* @param {string} eventName
* @param {function({data: Object})} handler
*/
u2f.WrappedIosPort_.prototype.addEventListener = function(eventName, handler) {
var name = eventName.toLowerCase();
if (name !== 'message') {
console.error('WrappedIosPort only supports message');
}
};
/**
* Sets up an embedded trampoline iframe, sourced from the extension.
* @param {function(MessagePort)} callback
* @private
*/
u2f.getIframePort_ = function(callback) {
// Create the iframe
var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID;
var iframe = document.createElement('iframe');
iframe.src = iframeOrigin + '/u2f-comms.html';
iframe.setAttribute('style', 'display:none');
document.body.appendChild(iframe);
var channel = new MessageChannel();
var ready = function(message) {
if (message.data == 'ready') {
channel.port1.removeEventListener('message', ready);
callback(channel.port1);
} else {
console.error('First event on iframe port was not "ready"');
}
};
channel.port1.addEventListener('message', ready);
channel.port1.start();
iframe.addEventListener('load', function() {
// Deliver the port to the iframe and initialize
//
setTimeout(function(){
iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]);
},1000);
});
};
//High-level JS API
/**
* Default extension response timeout in seconds.
* @const
*/
u2f.EXTENSION_TIMEOUT_SEC = 30;
/**
* A singleton instance for a MessagePort to the extension.
* @type {MessagePort|u2f.WrappedChromeRuntimePort_}
* @private
*/
u2f.port_ = null;
/**
* Callbacks waiting for a port
* @type {Array<function((MessagePort|u2f.WrappedChromeRuntimePort_))>}
* @private
*/
u2f.waitingForPort_ = [];
/**
* A counter for requestIds.
* @type {number}
* @private
*/
u2f.reqCounter_ = 0;
/**
* A map from requestIds to client callbacks
* @type {Object.<number,(function((u2f.Error|u2f.RegisterResponse))
* |function((u2f.Error|u2f.SignResponse)))>}
* @private
*/
u2f.callbackMap_ = {};
/**
* Creates or retrieves the MessagePort singleton to use.
* @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
* @private
*/
u2f.getPortSingleton_ = function(callback) {
if (u2f.port_) {
callback(u2f.port_);
} else {
if (u2f.waitingForPort_.length == 0) {
u2f.getMessagePort(function(port) {
u2f.port_ = port;
u2f.port_.addEventListener('message',
/** @type {function(Event)} */ (u2f.responseHandler_));
// Careful, here be async callbacks. Maybe.
while (u2f.waitingForPort_.length)
u2f.waitingForPort_.shift()(u2f.port_);
});
}
u2f.waitingForPort_.push(callback);
}
};
/**
* Handles response messages from the extension.
* @param {MessageEvent.<u2f.Response>} message
* @private
*/
u2f.responseHandler_ = function(message) {
var response = message.data;
var reqId = response['requestId'];
if (!reqId || !u2f.callbackMap_[reqId]) {
console.error('Unknown or missing requestId in response.');
return;
}
var cb = u2f.callbackMap_[reqId];
delete u2f.callbackMap_[reqId];
cb(response['responseData']);
};
/**
* Dispatches an array of sign requests to available U2F tokens.
* If the JS API version supported by the extension is unknown, it first sends a
* message to the extension to find out the supported API version and then it sends
* the sign request.
* @param {string=} appId
* @param {string=} challenge
* @param {Array<u2f.RegisteredKey>} registeredKeys
* @param {function((u2f.Error|u2f.SignResponse))} callback
* @param {number=} opt_timeoutSeconds
*/
u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
if (js_api_version === undefined) {
// Send a message to get the extension to JS API version, then send the actual sign request.
u2f.getApiVersion(
function (response) {
js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version'];
u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);
});
} else {
// We know the JS API version. Send the actual sign request in the supported API version.
u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);
}
};
/**
* Dispatches an array of sign requests to available U2F tokens.
* @param {string=} appId
* @param {string=} challenge
* @param {Array<u2f.RegisteredKey>} registeredKeys
* @param {function((u2f.Error|u2f.SignResponse))} callback
* @param {number=} opt_timeoutSeconds
*/
u2f.sendSignRequest = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
u2f.getPortSingleton_(function(port) {
var reqId = ++u2f.reqCounter_;
u2f.callbackMap_[reqId] = callback;
var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
var req = u2f.formatSignRequest_(appId, challenge, registeredKeys, timeoutSeconds, reqId);
port.postMessage(req);
});
};
/**
* Dispatches register requests to available U2F tokens. An array of sign
* requests identifies already registered tokens.
* If the JS API version supported by the extension is unknown, it first sends a
* message to the extension to find out the supported API version and then it sends
* the register request.
* @param {string=} appId
* @param {Array<u2f.RegisterRequest>} registerRequests
* @param {Array<u2f.RegisteredKey>} registeredKeys
* @param {function((u2f.Error|u2f.RegisterResponse))} callback
* @param {number=} opt_timeoutSeconds
*/
u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
if (js_api_version === undefined) {
// Send a message to get the extension to JS API version, then send the actual register request.
u2f.getApiVersion(
function (response) {
js_api_version = response['js_api_version'] === undefined ? 0: response['js_api_version'];
u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
callback, opt_timeoutSeconds);
});
} else {
// We know the JS API version. Send the actual register request in the supported API version.
u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
callback, opt_timeoutSeconds);
}
};
/**
* Dispatches register requests to available U2F tokens. An array of sign
* requests identifies already registered tokens.
* @param {string=} appId
* @param {Array<u2f.RegisterRequest>} registerRequests
* @param {Array<u2f.RegisteredKey>} registeredKeys
* @param {function((u2f.Error|u2f.RegisterResponse))} callback
* @param {number=} opt_timeoutSeconds
*/
u2f.sendRegisterRequest = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
u2f.getPortSingleton_(function(port) {
var reqId = ++u2f.reqCounter_;
u2f.callbackMap_[reqId] = callback;
var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
var req = u2f.formatRegisterRequest_(
appId, registeredKeys, registerRequests, timeoutSeconds, reqId);
port.postMessage(req);
});
};
/**
* Dispatches a message to the extension to find out the supported
* JS API version.
* If the user is on a mobile phone and is thus using Google Authenticator instead
* of the Chrome extension, don't send the request and simply return 0.
* @param {function((u2f.Error|u2f.GetJsApiVersionResponse))} callback
* @param {number=} opt_timeoutSeconds
*/
u2f.getApiVersion = function(callback, opt_timeoutSeconds) {
u2f.getPortSingleton_(function(port) {
// If we are using Android Google Authenticator or iOS client app,
// do not fire an intent to ask which JS API version to use.
if (port.getPortType) {
var apiVersion;
switch (port.getPortType()) {
case 'WrappedIosPort_':
case 'WrappedAuthenticatorPort_':
apiVersion = 1.1;
break;
default:
apiVersion = 0;
break;
}
callback({ 'js_api_version': apiVersion });
return;
}
var reqId = ++u2f.reqCounter_;
u2f.callbackMap_[reqId] = callback;
var req = {
type: u2f.MessageTypes.U2F_GET_API_VERSION_REQUEST,
timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ?
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC),
requestId: reqId
};
port.postMessage(req);
});
};

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIC5TCCAc2gAwIBAgIJAPAHlj0PtV3iMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0xODA3MDgwMTUzMTlaFw0xODA4MDcwMTUzMTlaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAM9uac6J8FOOaVFtQIWmjZKQbUs1k2SBHHYASE5hOqCw9sBjlGix2VC/yuk7
1Nn+N+QM59CkZxR33v2kEK1PTA5Ock/nOHljmnEQrDsKumpp6Jh/L3lB0uwHbTra
RTl7giJ4CRPdMc7EYZnK+TecKMaq5O7X8RG55UJoHZFxVIkc2LoTupW13NfAs09G
JdDSfMvLVy2ZqELlKQ4qR3kLE7RpDss7WKN0bqG3NebI5liG2cDdObP4yd4gBpKk
Dg8nwB4hrDaJaf4MzvrTefnd06Fkx2Fl/H0rZx7HOFMHEX+08fXcby7LMO3VbwEx
ChOlDvHiBUHJDLow67JzNq5lv+8CAwEAAaM6MDgwFAYDVR0RBA0wC4IJbG9jYWxo
b3N0MAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0B
AQsFAAOCAQEAa5rPgtqb8PJ/ZEE62T6kKaSZeT+ebFqdRvv24WYulbLbZGwryfhq
utzS2peNBq57LlwvI+bOFmPUdIJjHBX2EM9NV3B9nJliM22ri6HuiXFpNcD2ehJV
W2wMCNXZRyHySgM+7JIz/0TYEpJi3gq9P+IHtz4aXBZ5T2M+HsCBdnBci1n9EqTF
9rmt/2RPQxNDIWeAGMlq19cQmmGwVfjxtKIPBGJd7RxkgT3itSjVxb1wyQTlDsZc
0dJaMNGJDtGgpRtOlpC6Xv0SZ66GfsFwa8x5d/mty4m1kKYPu+77A6g9lBhjo8r6
5MFFjFoq0XZuW5bBLbxQUC6qR8+mRI7gNg==
-----END CERTIFICATE-----

View File

@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDPbmnOifBTjmlR
bUCFpo2SkG1LNZNkgRx2AEhOYTqgsPbAY5RosdlQv8rpO9TZ/jfkDOfQpGcUd979
pBCtT0wOTnJP5zh5Y5pxEKw7CrpqaeiYfy95QdLsB2062kU5e4IieAkT3THOxGGZ
yvk3nCjGquTu1/ERueVCaB2RcVSJHNi6E7qVtdzXwLNPRiXQ0nzLy1ctmahC5SkO
Kkd5CxO0aQ7LO1ijdG6htzXmyOZYhtnA3Tmz+MneIAaSpA4PJ8AeIaw2iWn+DM76
03n53dOhZMdhZfx9K2cexzhTBxF/tPH13G8uyzDt1W8BMQoTpQ7x4gVByQy6MOuy
czauZb/vAgMBAAECggEBAJpxv14E3meYNVpnWg8o/2gNGRNShLfkWeiSNUQGqLxU
hqSggDOjmAbDhrU50zWsTrkB0uhKQGXBCssXiUNBM4PDbOGJa0cWnjtpCpO7XSk2
SfShXhuOoxkSPoX/VmOCTlaTwU6E9zzYg0MbGYwKPWIOg/5B0kA8hTJ4iutr0mFz
Pc8xu4CkEnyNirDpkCuFvC5sVe9xaLgN6AvhvmKteDYxA/deJhpTx21IwOVyoCt1
axhfqUf8RnQJAShxuGyM0k9f1eErvFipU9DOYlyBWI1F44esfT8EoC2Muo/P8fWH
qgz7UU4SMHgGRj78nq2d+Xh4J+5o+UCLxWFmikzgA+ECgYEA6Jp3xVS868PDkh2U
q14sRo3x+m8QPqFjRV7fYnYKFkikQLZ8NwVXCPAaUeK4+clw1CDawaE/J0exp88z
r7ETKiQWQFhVBXPI4xHVu5GvZ5Cl3Bl5nog6nXA8XFRf2dwQmQWYsx3RLFl/WR1G
v4rqth/Al5fZep0MI8oJ2SesCYMCgYEA5EvDxiZ4XoNmVUROUSSdpsObWUhp7wxE
VsJGXu8sahWzlfF669nJh9oykU0X+VP7pqgzUG8IC2L/urFK4l/QzyklOgslkbLM
2Si2SwkTzqQHa03iDvMa43t6+Q575PMFXWJQCoCO6pvlx/cUrDrj0Zl7jul+4Z6I
qTM+5IHIICUCgYEAhORz128hfMXc/6cc4IeuXiUNAhrgOhg0wlaA5B0yiCa6iSp5
b2oCnt2m+++/5R5c8yVONmBs14FTk9+C1TcjDumNOPf+o13SgUEYTtDeWRpAWK7J
WHTWoEqWgd3G4Y6kgPi8lGFNt7vuUTizyg2RKbqcaz/bhf0iCXSOCd5roQcCgYEA
l5uA/Zwan5lfYgykfdp1H4QTLG2ce/WHhJ4roWvv8NtMkaz11p6g6SkMH9Y0r40I
N5rHyQiTLQyni1Gly0OaJZjLcgpo/qLgyu3wcVAr6TShAK+OCiODncIo3jCyMk75
JxTc1rydLEwvgmYhQmcxV8Y3eaaybh6b3tF7WMCfKEkCgYAqySNWMDbmAkPyCFAV
1EsbYB9Hh8zLJEDfQCwAbCH4BLzRwFx5SpvEQsNML/kf0i/iBPfPoC4cvnMTVzk7
1LY247FCFrtEV5TxObWQJPN45SjOiQeqhpHzBcCu+kh1fz99+VcqZRbmrGV4E4NY
reVBSr7HxTaQzJqfg0Ss/y5J0A==
-----END PRIVATE KEY-----

View File

@ -1,4 +0,0 @@
# Run this to make cert you can import in chrome, then run localhost with HTTPS
openssl req -x509 -out localhost.crt -keyout localhost.key -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' -extensions EXT -config <( \
printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")

View File

@ -1,15 +0,0 @@
# https://blog.anvileight.com/posts/simple-python-http-server/#python-3-x
from http.server import HTTPServer, SimpleHTTPRequestHandler
import ssl
host =('localhost', 4443)
httpd = HTTPServer(('localhost', 4443), SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket (httpd.socket,
keyfile="localhost.key",
certfile='localhost.crt', server_side=True)
print('serving on ', host)
httpd.serve_forever()