Compare commits
141 Commits
Author | SHA1 | Date | |
---|---|---|---|
0dfda6fce2 | |||
09b73d694f | |||
9ab5e761c3 | |||
b3604f49ba | |||
6ae1cd3865 | |||
f9d3b9561d | |||
ec98af115f | |||
fecf258116 | |||
437f691d12 | |||
55aadfd78e | |||
813eb97d2f | |||
32afdccfb3 | |||
41ae0e4a2c | |||
b0baace2e7 | |||
6ff4200f5d | |||
1fab0b8f1f | |||
ce96fffddd | |||
4fb25e165a | |||
8fc0da7934 | |||
494e856198 | |||
472b094acb | |||
e0ce23034f | |||
5f3974a4e6 | |||
26adac1730 | |||
eab8b81c95 | |||
325396d518 | |||
6d04c86018 | |||
56d6624e4e | |||
3094c87b0a | |||
212f98e384 | |||
73f538dd0e | |||
a5f794c0ff | |||
f28cf9c6d0 | |||
6068fb9868 | |||
6a288243c1 | |||
955d4f76ef | |||
74cbe00e3b | |||
7e490f17fc | |||
9bb706987f | |||
88a759566d | |||
44fa3bbb8e | |||
89e9296825 | |||
873d65b823 | |||
eb8e3ed46a | |||
8b97276e32 | |||
78579c27dc | |||
ca80329b4c | |||
7a49169492 | |||
46dd4fe818 | |||
c71bbd8689 | |||
7068be9cd5 | |||
f8635f1682 | |||
ffa9ad4923 | |||
5fc8d214fd | |||
5f49f4680e | |||
86393c46b4 | |||
4cc72bcd97 | |||
5e0edf3e11 | |||
bd810fff87 | |||
d9fb508949 | |||
c2b7acb6aa | |||
4690a7ce65 | |||
61f24d142d | |||
f5c6f99423 | |||
96de4f0850 | |||
331ebdfccf | |||
a6a6d653ad | |||
928bc0216d | |||
6d52c9ede7 | |||
89769ecc18 | |||
3b3f47bfcf | |||
6fa443b0bc | |||
893d4131b2 | |||
4e21c0bd8f | |||
251eb6bf64 | |||
08e236df69 | |||
d2091563ab | |||
54a6a82ca0 | |||
40b9dae38a | |||
98a209e330 | |||
d3b5fb68ee | |||
74a1f0e21b | |||
e21172fff8 | |||
9d3144e9b1 | |||
a2a774125f | |||
349ea5343a | |||
c851807376 | |||
84d1629aa3 | |||
8f6ae29163 | |||
a0d27c2c56 | |||
3a10427bd9 | |||
f3b591e570 | |||
175f59d206 | |||
f5ff6a11f0 | |||
d979420324 | |||
5076af1be4 | |||
0a7845459c | |||
c4262b0f5b | |||
53fb0059a7 | |||
a1a75e4ab5 | |||
d68011ef04 | |||
02e83073e0 | |||
3a48756f96 | |||
946e932b1e | |||
142d4002e5 | |||
dbe5283e1f | |||
2d233f164e | |||
b62e9906c7 | |||
e22e636475 | |||
074225d87a | |||
bb9b2ea9d4 | |||
e8d5bc5829 | |||
850381a633 | |||
ce3ad0e56f | |||
00d86379e5 | |||
6098810167 | |||
821880a8d6 | |||
44f96f5843 | |||
6ec9fb962a | |||
c9bfe001ee | |||
5e46fd96ac | |||
103cc3cfb0 | |||
9544330dc3 | |||
0964ff69b7 | |||
e4a2b9e1ca | |||
d29fa34da1 | |||
6ed2610a5c | |||
3b9d4e5023 | |||
2da083c18a | |||
50bfbc1eff | |||
86739df7a1 | |||
c7f0d050d7 | |||
b79670a447 | |||
169ba59ed4 | |||
f3003c58c9 | |||
084e518018 | |||
6674f0a8ff | |||
f704851419 | |||
0d5e1ee872 | |||
5cb81c753d | |||
b0b0564df9 |
1
ALPHA_VERSION
Normal file
1
ALPHA_VERSION
Normal file
@ -0,0 +1 @@
|
|||||||
|
2.0.0
|
27
CHANGELOG.md
27
CHANGELOG.md
@ -15,3 +15,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Extension interface to U2F and FIDO2
|
- Extension interface to U2F and FIDO2
|
||||||
- Read firmware version
|
- Read firmware version
|
||||||
- Read RNG bytes
|
- 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.
|
||||||
|
|
||||||
|
## [2.2.1] - 2019-04-23
|
||||||
|
|
||||||
|
- This minor release fixes some small issues. #179, #182
|
||||||
|
23
Makefile
23
Makefile
@ -9,7 +9,9 @@
|
|||||||
|
|
||||||
ecc_platform=2
|
ecc_platform=2
|
||||||
|
|
||||||
src = $(wildcard pc/*.c) $(wildcard fido2/*.c) $(wildcard crypto/sha256/*.c) crypto/tiny-AES-c/aes.c
|
src = $(wildcard pc/*.c) $(wildcard fido2/*.c) $(wildcard fido2/extensions/*.c) \
|
||||||
|
$(wildcard crypto/sha256/*.c) crypto/tiny-AES-c/aes.c
|
||||||
|
|
||||||
obj = $(src:.c=.o) crypto/micro-ecc/uECC.o
|
obj = $(src:.c=.o) crypto/micro-ecc/uECC.o
|
||||||
|
|
||||||
LIBCBOR = tinycbor/lib/libtinycbor.a
|
LIBCBOR = tinycbor/lib/libtinycbor.a
|
||||||
@ -20,7 +22,17 @@ else
|
|||||||
export LDFLAGS = -Wl,--gc-sections
|
export LDFLAGS = -Wl,--gc-sections
|
||||||
endif
|
endif
|
||||||
LDFLAGS += $(LIBCBOR)
|
LDFLAGS += $(LIBCBOR)
|
||||||
CFLAGS = -O2 -fdata-sections -ffunction-sections
|
|
||||||
|
VERSION:=$(shell git describe --abbrev=0 )
|
||||||
|
VERSION_FULL:=$(shell git describe)
|
||||||
|
VERSION_MAJ:=$(shell python -c 'print("$(VERSION)".split(".")[0])')
|
||||||
|
VERSION_MIN:=$(shell python -c 'print("$(VERSION)".split(".")[1])')
|
||||||
|
VERSION_PAT:=$(shell python -c 'print("$(VERSION)".split(".")[2])')
|
||||||
|
|
||||||
|
VERSION_FLAGS= -DSOLO_VERSION_MAJ=$(VERSION_MAJ) -DSOLO_VERSION_MIN=$(VERSION_MIN) \
|
||||||
|
-DSOLO_VERSION_PATCH=$(VERSION_PAT) -DSOLO_VERSION=\"$(VERSION_FULL)\"
|
||||||
|
|
||||||
|
CFLAGS = -O2 -fdata-sections -ffunction-sections $(VERSION_FLAGS) -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./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
|
INCLUDES += -I./crypto/cifra/src
|
||||||
@ -41,7 +53,7 @@ tinycbor/Makefile crypto/tiny-AES-c/aes.c:
|
|||||||
cbor: $(LIBCBOR)
|
cbor: $(LIBCBOR)
|
||||||
|
|
||||||
$(LIBCBOR):
|
$(LIBCBOR):
|
||||||
cd tinycbor/ && $(MAKE) clean && $(MAKE) -j8
|
cd tinycbor/ && $(MAKE) clean && $(MAKE) LDFLAGS='' -j8
|
||||||
|
|
||||||
version:
|
version:
|
||||||
@git describe
|
@git describe
|
||||||
@ -83,6 +95,11 @@ docker-build:
|
|||||||
docker run --rm -v "$(CURDIR)/builds:/builds" \
|
docker run --rm -v "$(CURDIR)/builds:/builds" \
|
||||||
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
|
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
|
||||||
$(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
|
$(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
|
||||||
|
uncached-docker-build:
|
||||||
|
docker build --no-cache -t $(DOCKER_IMAGE) .
|
||||||
|
docker run --rm -v "$(CURDIR)/builds:/builds" \
|
||||||
|
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
|
||||||
|
$(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
|
||||||
|
|
||||||
CPPCHECK_FLAGS=--quiet --error-exitcode=2
|
CPPCHECK_FLAGS=--quiet --error-exitcode=2
|
||||||
|
|
||||||
|
41
README.md
41
README.md
@ -4,6 +4,12 @@
|
|||||||
[](https://keybase.io/team/solokeys.public)
|
[](https://keybase.io/team/solokeys.public)
|
||||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo?ref=badge_shield)
|
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo?ref=badge_shield)
|
||||||
|
|
||||||
|
[](https://github.com/solokeys/solo/releases)
|
||||||
|
[](https://github.com/solokeys/solo/commits/master)
|
||||||
|
[](https://github.com/solokeys/solo/commits/master)
|
||||||
|
[](https://github.com/solokeys/solo/commits/master)
|
||||||
|
[](https://github.com/solokeys/solo/graphs/contributors)
|
||||||
|
|
||||||
|
|
||||||
# Solo
|
# Solo
|
||||||
|
|
||||||
@ -13,7 +19,7 @@ Solo supports FIDO2 and U2F standards for strong two-factor authentication and p
|
|||||||
|
|
||||||
<img src="https://solokeys.com/images/photos/hero-on-white-cropped.png" width="600">
|
<img src="https://solokeys.com/images/photos/hero-on-white-cropped.png" width="600">
|
||||||
|
|
||||||
This repo contains the Solo firmware, including implementations of FIDO2 and U2F (CTAP2 and CTAP) over USB and NFC. The main implementation is for STM32L432, and it's ported to NRF52840 and EFM32J.
|
This repo contains the Solo firmware, including implementations of FIDO2 and U2F (CTAP2 and CTAP) over USB and NFC. The main implementation is for STM32L432, but it is easily portable.
|
||||||
|
|
||||||
For development no hardware is needed, Solo also runs as a standalone application for Windows, Linux, and Mac OSX. If you like (or want to learn) hardware instead, you can run Solo on the NUCLEO-L432KC development board, or we make Solo for Hacker, an unlocked version of Solo that lets you customize its firmware.
|
For development no hardware is needed, Solo also runs as a standalone application for Windows, Linux, and Mac OSX. If you like (or want to learn) hardware instead, you can run Solo on the NUCLEO-L432KC development board, or we make Solo for Hacker, an unlocked version of Solo that lets you customize its firmware.
|
||||||
|
|
||||||
@ -33,7 +39,7 @@ Solo is based on the STM32L432 microcontroller. It offers the following security
|
|||||||
|
|
||||||
Solo for Hacker is a special version of Solo that let you customize its firmware, for example you can change the LED color, and even build advanced applications.
|
Solo for Hacker is a special version of Solo that let you customize its firmware, for example you can change the LED color, and even build advanced applications.
|
||||||
|
|
||||||
You can only buy Solo for Hacker at [solokeys.com](https://solokeys.com), as we don't sell it on Amazon and other places to avoid confusing customers. If you buy a Hacker, you can permanently lock it into a regular Solo, but viceversa you can NOT take a regular Solo and turn it a Hacker.
|
Check out [solokeys.com](https://solokeys.com), for options on where to buy Solo. Solo Hacker can be converted to a secure version, but normal Solo cannot be converted to a Hacker version.
|
||||||
|
|
||||||
If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/). 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 only support Python3.
|
||||||
|
|
||||||
@ -56,17 +62,14 @@ Alternatively, run `make docker-build` and use the firmware generated in `/tmp`.
|
|||||||
|
|
||||||
If you forgot the `--recurse-submodules` when cloning, simply `git submodule update --init --recursive`.
|
If you forgot the `--recurse-submodules` when cloning, simply `git submodule update --init --recursive`.
|
||||||
|
|
||||||
For example, if you want to turn off any blue light emission, you can edit [`led_rgb()`](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/led.c#L15) and force:
|
For example, if you want to turn off any blue light emission, you can edit [`led_rgb()`](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/app.h#L48) and change `LED_INIT_VALUE`
|
||||||
```
|
to be a different hex color.
|
||||||
uint32_t b = 0;
|
|
||||||
```
|
|
||||||
|
|
||||||
Then recompile, load your new firmware, and enjoy a blue-light-free version of Solo.
|
Then recompile, load your new firmware, and enjoy a different LED color Solo.
|
||||||
|
|
||||||
In the Hacker version, hardware is the same and firmware is unlocked, in the sense that you can 1) load an unsigned application, or 2) entirely reflash the key. By contrast, in a regular Solo you can only upgrade to a firmware signed by SoloKeys, and flash is locked and debug disabled permanently.
|
In the Hacker version, hardware is the same but the firmware is unlocked, so you can 1) load an unsigned application, or 2) entirely reflash the key. By contrast, in a regular Solo you can only upgrade to a firmware signed by SoloKeys, and flash is locked and debug disabled permanently.
|
||||||
|
|
||||||
A frequently asked question is whether Solo for Hacker is less secure than regular Solo. The answer is certainly yes, and therefore we only recommend to use Solo for Hacker for development, experimentation, and fun. An attacker with physical access to a Solo for Hacker can reflash it following the steps above, and even a malware on your computer could possibly reflash it.
|
|
||||||
|
|
||||||
|
Hacker Solo isn't really secure so you should only use it for development. An attacker with physical access to a Solo for Hacker can reflash it following the steps above, and even a malware on your computer could possibly reflash it.
|
||||||
|
|
||||||
# Developing Solo (No Hardware Needed)
|
# Developing Solo (No Hardware Needed)
|
||||||
|
|
||||||
@ -83,7 +86,7 @@ This builds Solo as a standalone application. Solo application is set up to send
|
|||||||
Testing can be done using our fork of Yubico's client software, python-fido2. Our fork of python-fido2 has small changes to make it send USB HID over UDP to the authenticator application. You can install our fork by running the following:
|
Testing can be done using our fork of Yubico's client software, python-fido2. Our fork of python-fido2 has small changes to make it send USB HID over UDP to the authenticator application. You can install our fork by running the following:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd python-fido2 && python setup.py install
|
pip install -r tools/requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
Run the Solo application:
|
Run the Solo application:
|
||||||
@ -93,12 +96,7 @@ Run the Solo application:
|
|||||||
|
|
||||||
In another shell, you can run client software, for example our tests:
|
In another shell, you can run client software, for example our tests:
|
||||||
```bash
|
```bash
|
||||||
python tools/ctap_test.py
|
python tools/ctap_test.py sim fido2
|
||||||
```
|
|
||||||
|
|
||||||
Or any client example such as:
|
|
||||||
```bash
|
|
||||||
python python-fido2/examples/credential.py
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can find more details in our [documentation](https://docs.solokeys.io/solo/), including how to build on the the NUCLEO-L432KC development board.
|
You can find more details in our [documentation](https://docs.solokeys.io/solo/), including how to build on the the NUCLEO-L432KC development board.
|
||||||
@ -121,8 +119,15 @@ Look at the issues to see what is currently being worked on. Feel free to add is
|
|||||||
# License
|
# License
|
||||||
|
|
||||||
Solo is fully open source.
|
Solo is fully open source.
|
||||||
|
|
||||||
All software, unless otherwise noted, is dual licensed under Apache 2.0 and MIT.
|
All software, unless otherwise noted, is dual licensed under Apache 2.0 and MIT.
|
||||||
You may use Solo under the terms of either the Apache 2.0 license or MIT license.
|
You may use Solo software under the terms of either the Apache 2.0 license or MIT license.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
All documentation, unless otherwise noted, is licensed under CC-BY-SA.
|
||||||
|
You may use Solo documentation under the terms of the CC-BY-SA 4.0 license
|
||||||
|
|
||||||
|
|
||||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo?ref=badge_large)
|
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo?ref=badge_large)
|
||||||
|
@ -1 +1 @@
|
|||||||
1.1.1
|
2.2.1
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
To build, develop and debug the firmware for the STM32L432. This will work
|
To build, develop and debug the firmware for the STM32L432. This will work
|
||||||
for Solo Hacker, the Nucleo development board, or you own homemade Solo.
|
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)).
|
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)).
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ There exists a development board [NUCLEO-L432KC](https://www.st.com/en/evaluatio
|
|||||||
|
|
||||||
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.
|
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 manage like `apt-get` or `pacman`,
|
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`.
|
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.
|
To program your build, you'll need one of the following programs.
|
||||||
@ -52,7 +52,7 @@ make build-hacker DEBUG=1
|
|||||||
```
|
```
|
||||||
|
|
||||||
If you use `DEBUG=2`, that means Solo will not boot until something starts reading
|
If you use `DEBUG=2`, that means Solo will not boot until something starts reading
|
||||||
it's debug messages. So it basically it waits to tether to a serial terminal so that you don't
|
its debug messages. So it basically waits to tether to a serial terminal so that you don't
|
||||||
miss any debug messages.
|
miss any debug messages.
|
||||||
|
|
||||||
We recommend using our `solo` tool as a serial emulator since it will automatically
|
We recommend using our `solo` tool as a serial emulator since it will automatically
|
||||||
|
@ -5,22 +5,22 @@ and easy to understand, especially when paired with a high level overview.
|
|||||||
|
|
||||||
## FIDO2 codebase
|
## FIDO2 codebase
|
||||||
|
|
||||||
* main.c - calls high level functions and implements event loop.
|
* `main.c` - calls high level functions and implements event loop.
|
||||||
|
|
||||||
* ctaphid.c - implements [USBHID protocol](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#usb) for FIDO.
|
* `ctaphid.c` - implements [USBHID protocol](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#usb) for FIDO.
|
||||||
|
|
||||||
* u2f.c - implements [U2F protocol](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html).
|
* `u2f.c` - implements [U2F protocol](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html).
|
||||||
|
|
||||||
* ctap.c - implements [CTAP2 protocol](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html).
|
* `ctap.c` - implements [CTAP2 protocol](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html).
|
||||||
|
|
||||||
* ctap_parse.c - implements parsing for CTAP protocol.
|
* `ctap_parse.c` - implements parsing for CTAP protocol.
|
||||||
* this could use some work minimizing.
|
* this could use some work minimizing.
|
||||||
|
|
||||||
* log.c - embedded friendly debug logging.
|
* `log.c` - embedded friendly debug logging.
|
||||||
|
|
||||||
* crypto.c - software implementation of the crypto needs of the application. Generally this will be copied and edited for different platforms. API defined in crypto.h should be the same.
|
* `crypto.c` - software implementation of the crypto needs of the application. Generally this will be copied and edited for different platforms. API defined in `crypto.h` should be the same.
|
||||||
|
|
||||||
* device.h - definitions of functions that are platform specific and should be implemented separately. See device.c in any of the implementations to see examples.
|
* `device.h` - definitions of functions that are platform specific and should be implemented separately. See `device.c` in any of the implementations to see examples.
|
||||||
|
|
||||||
## Data flow
|
## Data flow
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ Documentation of the `master` branch is deployed to Netlify automatically.
|
|||||||
To host or develop locally:
|
To host or develop locally:
|
||||||
|
|
||||||
```
|
```
|
||||||
pip install mkdocs mkdocs-material
|
pip install mkdocs mkdocs-material markdown-include
|
||||||
```
|
```
|
||||||
|
|
||||||
`mkdocs serve` and visit [localhost:8000](http://localhost:8000).
|
`mkdocs serve` and visit [localhost:8000](http://localhost:8000).
|
||||||
|
@ -22,8 +22,8 @@ for FIDO2 operation.
|
|||||||
When you register a service with a FIDO2 or U2F authenticator, the
|
When you register a service with a FIDO2 or U2F authenticator, the
|
||||||
authenticator must generate a new keypair unique to that service. This keypair
|
authenticator must generate a new keypair unique to that service. This keypair
|
||||||
could be stored on the authenticator to be used in subsequent authentications,
|
could be stored on the authenticator to be used in subsequent authentications,
|
||||||
but now a certain amount of memory needs to be allocated for this. On embedded
|
but a certain amount of memory would need to be allocated for this. On embedded
|
||||||
devices, there isn't much memory to spare and users will allows frustratingly
|
devices, there isn't much memory to spare and users would frustratingly
|
||||||
hit the limit of this memory.
|
hit the limit of this memory.
|
||||||
|
|
||||||
The answer to this problem is to do key wrapping. The authenticator just
|
The answer to this problem is to do key wrapping. The authenticator just
|
||||||
@ -39,7 +39,7 @@ In essence, the following happens at registration.
|
|||||||
3. Return `P` and `R` to service. (`R` is in `KEYID` parameter)
|
3. Return `P` and `R` to service. (`R` is in `KEYID` parameter)
|
||||||
4. Service stores `P` and `R`.
|
4. Service stores `P` and `R`.
|
||||||
|
|
||||||
Now on authenication.
|
Now on authentication.
|
||||||
|
|
||||||
1. Service issues authentication request with `R` in `KEYID` parameter.
|
1. Service issues authentication request with `R` in `KEYID` parameter.
|
||||||
2. \* Authenticator generates `K` by calculating `HMAC(M,R)`.
|
2. \* Authenticator generates `K` by calculating `HMAC(M,R)`.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Welcome to the technical documentation for [solokeys/solo](https://github.com/solokeys/solo).
|
Welcome to the technical documentation for [solokeys/solo](https://github.com/solokeys/solo).
|
||||||
|
|
||||||
For now, you can read the repository `README.md`, more documentation to come!
|
Use the table of contents on the left to browse this documentation.
|
||||||
|
|
||||||
|
@ -1,24 +1,31 @@
|
|||||||
# tl;dr
|
# Summary
|
||||||
|
|
||||||
Create a file like [`/etc/udev/rules.d/99-solo.rules`](https://github.com/solokeys/solo/blob/master/99-solo.rules), for instance the following rules should cover access in all cases:
|
On Linux, by default USB dongles can't be accessed by users, for security reasons. To allow user access, so-called "udev rules" must be installed. (Under Fedora, your key may work without such a rule.)
|
||||||
|
|
||||||
|
Create a file like [`70-solokeys-access.rules`](https://github.com/solokeys/solo/blob/master/udev/70-solokeys-access.rules) in your `/etc/udev/rules.d` directory, for instance the following rule should cover normal access (it has to be on one line):
|
||||||
|
|
||||||
```
|
```
|
||||||
# Solo bootloader + firmware
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", MODE="0660", GROUP="plugdev"
|
||||||
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", GROUP="plugdev"
|
|
||||||
|
|
||||||
# ST DFU bootloader
|
|
||||||
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", TAG+="uaccess", GROUP="plugdev"
|
|
||||||
|
|
||||||
# U2F Zero
|
|
||||||
ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess", GROUP="plugdev"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Then run
|
Additionally, run the following command after you create this file (it is not necessary to do this again in the future):
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo udevadm control --reload-rules && sudo udevadm trigger
|
sudo udevadm control --reload-rules && sudo udevadm trigger
|
||||||
```
|
```
|
||||||
|
|
||||||
|
A simple way to setup both the udev rule and the udevadm reload is:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone git@github.com:solokeys/solo.git
|
||||||
|
cd solo/udev
|
||||||
|
make setup
|
||||||
|
```
|
||||||
|
|
||||||
|
We are working on getting user access to Solo keys enabled automatically in common Linux distributions: <https://github.com/solokeys/solo/issues/144>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# How do udev rules work and why are they needed
|
# How do udev rules work and why are they needed
|
||||||
|
|
||||||
In Linux, `udev` (part of `systemd`, read `man 7 udev`) handles "hot-pluggable" devices, of which Solo and U2F Zero are examples. In particular, it creates nodes in the `/dev` filesystem (in Linux, everything is a file), which allow accessing the device.
|
In Linux, `udev` (part of `systemd`, read `man 7 udev`) handles "hot-pluggable" devices, of which Solo and U2F Zero are examples. In particular, it creates nodes in the `/dev` filesystem (in Linux, everything is a file), which allow accessing the device.
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#define COSE_KEY_KTY_EC2 2
|
#define COSE_KEY_KTY_EC2 2
|
||||||
#define COSE_KEY_CRV_P256 1
|
#define COSE_KEY_CRV_P256 1
|
||||||
|
|
||||||
|
#define COSE_ALG_ES256 -7
|
||||||
#define COSE_ALG_ES256 -7
|
#define COSE_ALG_ECDH_ES_HKDF_256 -25
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
607
fido2/ctap.c
607
fido2/ctap.c
@ -11,6 +11,7 @@
|
|||||||
#include "cbor.h"
|
#include "cbor.h"
|
||||||
|
|
||||||
#include "ctap.h"
|
#include "ctap.h"
|
||||||
|
#include "u2f.h"
|
||||||
#include "ctaphid.h"
|
#include "ctaphid.h"
|
||||||
#include "ctap_parse.h"
|
#include "ctap_parse.h"
|
||||||
#include "ctap_errors.h"
|
#include "ctap_errors.h"
|
||||||
@ -33,19 +34,9 @@ static int8_t PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS;
|
|||||||
|
|
||||||
AuthenticatorState STATE;
|
AuthenticatorState STATE;
|
||||||
|
|
||||||
|
|
||||||
static void ctap_reset_key_agreement();
|
static void ctap_reset_key_agreement();
|
||||||
|
|
||||||
static struct {
|
struct _getAssertionState getAssertionState;
|
||||||
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;
|
|
||||||
|
|
||||||
uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash)
|
uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash)
|
||||||
{
|
{
|
||||||
@ -69,6 +60,8 @@ uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint8_t ctap_get_info(CborEncoder * encoder)
|
uint8_t ctap_get_info(CborEncoder * encoder)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -77,16 +70,14 @@ uint8_t ctap_get_info(CborEncoder * encoder)
|
|||||||
CborEncoder options;
|
CborEncoder options;
|
||||||
CborEncoder pins;
|
CborEncoder pins;
|
||||||
|
|
||||||
const int number_of_versions = 2;
|
ret = cbor_encoder_create_map(encoder, &map, 6);
|
||||||
|
|
||||||
ret = cbor_encoder_create_map(encoder, &map, 5);
|
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
{
|
{
|
||||||
|
|
||||||
ret = cbor_encode_uint(&map, RESP_versions); // versions key
|
ret = cbor_encode_uint(&map, RESP_versions); // versions key
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
{
|
{
|
||||||
ret = cbor_encoder_create_array(&map, &array, number_of_versions);
|
ret = cbor_encoder_create_array(&map, &array, 2);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
{
|
{
|
||||||
ret = cbor_encode_text_stringz(&array, "U2F_V2");
|
ret = cbor_encode_text_stringz(&array, "U2F_V2");
|
||||||
@ -98,6 +89,19 @@ uint8_t ctap_get_info(CborEncoder * encoder)
|
|||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = cbor_encode_uint(&map, RESP_extensions);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encoder_create_array(&map, &array, 1);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encode_text_stringz(&array, "hmac-secret");
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
ret = cbor_encoder_close_container(&map, &array);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
|
||||||
ret = cbor_encode_uint(&map, RESP_aaguid);
|
ret = cbor_encode_uint(&map, RESP_aaguid);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
{
|
{
|
||||||
@ -105,6 +109,57 @@ uint8_t ctap_get_info(CborEncoder * encoder)
|
|||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = cbor_encode_uint(&map, RESP_options);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encoder_create_map(&map, &options,4);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encode_text_string(&options, "rk", 2);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encode_boolean(&options, 1); // Capable of storing keys locally
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cbor_encode_text_string(&options, "up", 2);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encode_boolean(&options, 1); // Capable of testing user presence
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOT [yet] capable of verifying user
|
||||||
|
// Do not add option if UV isn't supported.
|
||||||
|
//
|
||||||
|
// ret = cbor_encode_text_string(&options, "uv", 2);
|
||||||
|
// check_ret(ret);
|
||||||
|
// {
|
||||||
|
// ret = cbor_encode_boolean(&options, 0);
|
||||||
|
// check_ret(ret);
|
||||||
|
// }
|
||||||
|
|
||||||
|
ret = cbor_encode_text_string(&options, "plat", 4);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encode_boolean(&options, 0); // Not attached to platform
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ret = cbor_encode_text_string(&options, "clientPin", 9);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encode_boolean(&options, ctap_is_pin_set());
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
ret = cbor_encoder_close_container(&map, &options);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
|
||||||
ret = cbor_encode_uint(&map, RESP_maxMsgSize);
|
ret = cbor_encode_uint(&map, RESP_maxMsgSize);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
{
|
{
|
||||||
@ -127,54 +182,7 @@ uint8_t ctap_get_info(CborEncoder * encoder)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
ret = cbor_encode_uint(&map, RESP_options);
|
|
||||||
check_ret(ret);
|
|
||||||
{
|
|
||||||
ret = cbor_encoder_create_map(&map, &options,4);
|
|
||||||
check_ret(ret);
|
|
||||||
{
|
|
||||||
ret = cbor_encode_text_string(&options, "plat", 4);
|
|
||||||
check_ret(ret);
|
|
||||||
{
|
|
||||||
ret = cbor_encode_boolean(&options, 0); // Not attached to platform
|
|
||||||
check_ret(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = cbor_encode_text_string(&options, "rk", 2);
|
|
||||||
check_ret(ret);
|
|
||||||
{
|
|
||||||
ret = cbor_encode_boolean(&options, 1); // Capable of storing keys locally
|
|
||||||
check_ret(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = cbor_encode_text_string(&options, "up", 2);
|
|
||||||
check_ret(ret);
|
|
||||||
{
|
|
||||||
ret = cbor_encode_boolean(&options, 1); // Capable of testing user presence
|
|
||||||
check_ret(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOT [yet] capable of verifying user
|
|
||||||
// Do not add option if UV isn't supported.
|
|
||||||
//
|
|
||||||
// ret = cbor_encode_text_string(&options, "uv", 2);
|
|
||||||
// check_ret(ret);
|
|
||||||
// {
|
|
||||||
// ret = cbor_encode_boolean(&options, 0);
|
|
||||||
// check_ret(ret);
|
|
||||||
// }
|
|
||||||
ret = cbor_encode_text_string(&options, "clientPin", 9);
|
|
||||||
check_ret(ret);
|
|
||||||
{
|
|
||||||
ret = cbor_encode_boolean(&options, ctap_is_pin_set());
|
|
||||||
check_ret(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
ret = cbor_encoder_close_container(&map, &options);
|
|
||||||
check_ret(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -311,18 +319,130 @@ static int is_matching_rk(CTAP_residentKey * rk, CTAP_residentKey * rk2)
|
|||||||
(rk->user.id_size == rk2->user.id_size);
|
(rk->user.id_size == rk2->user.id_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf, unsigned int * ext_encoder_buf_size)
|
||||||
|
{
|
||||||
|
CborEncoder extensions;
|
||||||
|
int ret;
|
||||||
|
uint8_t output[64];
|
||||||
|
uint8_t shared_secret[32];
|
||||||
|
uint8_t hmac[32];
|
||||||
|
uint8_t credRandom[32];
|
||||||
|
|
||||||
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, unsigned int len, CTAP_userEntity * user, uint8_t credtype, int32_t algtype, int32_t * sz, int store)
|
if (ext->hmac_secret_present == EXT_HMAC_SECRET_PARSED)
|
||||||
|
{
|
||||||
|
printf1(TAG_CTAP, "Processing hmac-secret..\r\n");
|
||||||
|
|
||||||
|
crypto_ecc256_shared_secret((uint8_t*) &ext->hmac_secret.keyAgreement.pubkey,
|
||||||
|
KEY_AGREEMENT_PRIV,
|
||||||
|
shared_secret);
|
||||||
|
crypto_sha256_init();
|
||||||
|
crypto_sha256_update(shared_secret, 32);
|
||||||
|
crypto_sha256_final(shared_secret);
|
||||||
|
|
||||||
|
crypto_sha256_hmac_init(shared_secret, 32, hmac);
|
||||||
|
crypto_sha256_update(ext->hmac_secret.saltEnc, ext->hmac_secret.saltLen);
|
||||||
|
crypto_sha256_hmac_final(shared_secret, 32, hmac);
|
||||||
|
|
||||||
|
if (memcmp(ext->hmac_secret.saltAuth, hmac, 16) == 0)
|
||||||
|
{
|
||||||
|
printf1(TAG_CTAP, "saltAuth is valid\r\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf1(TAG_CTAP, "saltAuth is invalid\r\n");
|
||||||
|
return CTAP2_ERR_EXTENSION_FIRST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate credRandom
|
||||||
|
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY, 0, credRandom);
|
||||||
|
crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId));
|
||||||
|
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY, 0, credRandom);
|
||||||
|
|
||||||
|
// Decrypt saltEnc
|
||||||
|
crypto_aes256_init(shared_secret, NULL);
|
||||||
|
crypto_aes256_decrypt(ext->hmac_secret.saltEnc, ext->hmac_secret.saltLen);
|
||||||
|
|
||||||
|
// Generate outputs
|
||||||
|
crypto_sha256_hmac_init(credRandom, 32, output);
|
||||||
|
crypto_sha256_update(ext->hmac_secret.saltEnc, 32);
|
||||||
|
crypto_sha256_hmac_final(credRandom, 32, output);
|
||||||
|
|
||||||
|
if (ext->hmac_secret.saltLen == 64)
|
||||||
|
{
|
||||||
|
crypto_sha256_hmac_init(credRandom, 32, output + 32);
|
||||||
|
crypto_sha256_update(ext->hmac_secret.saltEnc + 32, 32);
|
||||||
|
crypto_sha256_hmac_final(credRandom, 32, output + 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt for final output
|
||||||
|
crypto_aes256_init(shared_secret, NULL);
|
||||||
|
crypto_aes256_encrypt(output, ext->hmac_secret.saltLen);
|
||||||
|
|
||||||
|
// output
|
||||||
|
cbor_encoder_init(&extensions, ext_encoder_buf, *ext_encoder_buf_size, 0);
|
||||||
|
{
|
||||||
|
CborEncoder hmac_secret_map;
|
||||||
|
ret = cbor_encoder_create_map(&extensions, &hmac_secret_map, 1);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encode_text_stringz(&hmac_secret_map, "hmac-secret");
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
ret = cbor_encode_byte_string(&hmac_secret_map, output, ext->hmac_secret.saltLen);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
ret = cbor_encoder_close_container(&extensions, &hmac_secret_map);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
*ext_encoder_buf_size = cbor_encoder_get_buffer_size(&extensions, ext_encoder_buf);
|
||||||
|
}
|
||||||
|
else if (ext->hmac_secret_present == EXT_HMAC_SECRET_REQUESTED)
|
||||||
|
{
|
||||||
|
cbor_encoder_init(&extensions, ext_encoder_buf, *ext_encoder_buf_size, 0);
|
||||||
|
{
|
||||||
|
CborEncoder hmac_secret_map;
|
||||||
|
ret = cbor_encoder_create_map(&extensions, &hmac_secret_map, 1);
|
||||||
|
check_ret(ret);
|
||||||
|
{
|
||||||
|
ret = cbor_encode_text_stringz(&hmac_secret_map, "hmac-secret");
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
ret = cbor_encode_boolean(&hmac_secret_map, 1);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
ret = cbor_encoder_close_container(&extensions, &hmac_secret_map);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
*ext_encoder_buf_size = cbor_encoder_get_buffer_size(&extensions, ext_encoder_buf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*ext_encoder_buf_size = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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 ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo)
|
||||||
{
|
{
|
||||||
CborEncoder cose_key;
|
CborEncoder cose_key;
|
||||||
int auth_data_sz, ret;
|
|
||||||
|
unsigned int auth_data_sz = sizeof(CTAP_authDataHeader);
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
CTAP_residentKey rk, rk2;
|
CTAP_residentKey rk, rk2;
|
||||||
CTAP_authData * authData = (CTAP_authData *)auth_data_buf;
|
CTAP_authData * authData = (CTAP_authData *)auth_data_buf;
|
||||||
|
|
||||||
uint8_t * cose_key_buf = auth_data_buf + sizeof(CTAP_authData);
|
uint8_t * cose_key_buf = auth_data_buf + sizeof(CTAP_authData);
|
||||||
|
|
||||||
if((sizeof(CTAP_authDataHeader)) > len)
|
if((sizeof(CTAP_authDataHeader)) > *len)
|
||||||
{
|
{
|
||||||
printf1(TAG_ERR,"assertion fail, auth_data_buf must be at least %d bytes\n", sizeof(CTAP_authData) - sizeof(CTAP_attestHeader));
|
printf1(TAG_ERR,"assertion fail, auth_data_buf must be at least %d bytes\n", sizeof(CTAP_authData) - sizeof(CTAP_attestHeader));
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -332,18 +452,13 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
|||||||
crypto_sha256_update(rp->id, rp->size);
|
crypto_sha256_update(rp->id, rp->size);
|
||||||
crypto_sha256_final(authData->head.rpIdHash);
|
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);
|
count = auth_data_update_count(&authData->head);
|
||||||
|
|
||||||
device_set_status(CTAPHID_STATUS_UPNEEDED);
|
device_set_status(CTAPHID_STATUS_UPNEEDED);
|
||||||
// if NFC - not need to click a button
|
|
||||||
int but = 1;
|
int but;
|
||||||
if(!device_is_nfc())
|
|
||||||
{
|
but = ctap_user_presence_test();
|
||||||
but = ctap_user_presence_test();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!but)
|
if (!but)
|
||||||
{
|
{
|
||||||
@ -359,13 +474,12 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
|||||||
authData->head.flags |= (ctap_is_pin_set() << 2);
|
authData->head.flags |= (ctap_is_pin_set() << 2);
|
||||||
|
|
||||||
|
|
||||||
|
if (credInfo != NULL)
|
||||||
if (credtype != 0)
|
|
||||||
{
|
{
|
||||||
// add attestedCredentialData
|
// add attestedCredentialData
|
||||||
authData->head.flags |= (1 << 6);//include attestation data
|
authData->head.flags |= (1 << 6);//include attestation data
|
||||||
|
|
||||||
cbor_encoder_init(&cose_key, cose_key_buf, len - sizeof(CTAP_authData), 0);
|
cbor_encoder_init(&cose_key, cose_key_buf, *len - sizeof(CTAP_authData), 0);
|
||||||
|
|
||||||
memmove(authData->attest.aaguid, CTAP_AAGUID, 16);
|
memmove(authData->attest.aaguid, CTAP_AAGUID, 16);
|
||||||
authData->attest.credLenL = sizeof(CredentialId) & 0x00FF;
|
authData->attest.credLenL = sizeof(CredentialId) & 0x00FF;
|
||||||
@ -383,10 +497,10 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
|||||||
make_auth_tag(authData->head.rpIdHash, authData->attest.id.nonce, count, authData->attest.id.tag);
|
make_auth_tag(authData->head.rpIdHash, authData->attest.id.nonce, count, authData->attest.id.tag);
|
||||||
|
|
||||||
// resident key
|
// resident key
|
||||||
if (store)
|
if (credInfo->rk)
|
||||||
{
|
{
|
||||||
memmove(&rk.id, &authData->attest.id, sizeof(CredentialId));
|
memmove(&rk.id, &authData->attest.id, sizeof(CredentialId));
|
||||||
memmove(&rk.user, user, sizeof(CTAP_userEntity));
|
memmove(&rk.user, &credInfo->user, sizeof(CTAP_userEntity));
|
||||||
|
|
||||||
unsigned int index = STATE.rk_stored;
|
unsigned int index = STATE.rk_stored;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
@ -410,29 +524,19 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
|||||||
}
|
}
|
||||||
done_rk:
|
done_rk:
|
||||||
|
|
||||||
// DELETE
|
|
||||||
//crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL);
|
|
||||||
//crypto_aes256_encrypt((uint8_t*)&authData->attest.credential.user, CREDENTIAL_ENC_SIZE);
|
|
||||||
printf1(TAG_GREEN, "MADE credId: "); dump_hex1(TAG_GREEN, (uint8_t*) &authData->attest.id, sizeof(CredentialId));
|
printf1(TAG_GREEN, "MADE credId: "); dump_hex1(TAG_GREEN, (uint8_t*) &authData->attest.id, sizeof(CredentialId));
|
||||||
|
|
||||||
ctap_generate_cose_key(&cose_key, (uint8_t*)&authData->attest.id, sizeof(CredentialId), credtype, algtype);
|
ctap_generate_cose_key(&cose_key, (uint8_t*)&authData->attest.id, sizeof(CredentialId), credInfo->publicKeyCredentialType, credInfo->COSEAlgorithmIdentifier);
|
||||||
|
|
||||||
auth_data_sz = sizeof(CTAP_authData) + cbor_encoder_get_buffer_size(&cose_key, cose_key_buf);
|
auth_data_sz = sizeof(CTAP_authData) + cbor_encoder_get_buffer_size(&cose_key, cose_key_buf);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
auth_data_sz = sizeof(CTAP_authDataHeader);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
ret = cbor_encode_int(map,RESP_authData);
|
|
||||||
check_ret(ret);
|
|
||||||
ret = cbor_encode_byte_string(map, auth_data_buf, auth_data_sz);
|
|
||||||
check_ret(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sz) *sz = auth_data_sz;
|
|
||||||
|
|
||||||
|
|
||||||
|
*len = auth_data_sz;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,11 +649,30 @@ uint8_t ctap_add_attest_statement(CborEncoder * map, uint8_t * sigder, int len)
|
|||||||
// Return 1 if credential belongs to this token
|
// Return 1 if credential belongs to this token
|
||||||
int ctap_authenticate_credential(struct rpId * rp, CTAP_credentialDescriptor * desc)
|
int ctap_authenticate_credential(struct rpId * rp, CTAP_credentialDescriptor * desc)
|
||||||
{
|
{
|
||||||
|
uint8_t rpIdHash[32];
|
||||||
uint8_t tag[16];
|
uint8_t tag[16];
|
||||||
|
|
||||||
make_auth_tag(desc->credential.id.rpIdHash, desc->credential.id.nonce, desc->credential.id.count, tag);
|
switch(desc->type)
|
||||||
|
{
|
||||||
|
case PUB_KEY_CRED_PUB_KEY:
|
||||||
|
make_auth_tag(desc->credential.id.rpIdHash, desc->credential.id.nonce, desc->credential.id.count, tag);
|
||||||
|
return (memcmp(desc->credential.id.tag, tag, CREDENTIAL_TAG_SIZE) == 0);
|
||||||
|
break;
|
||||||
|
case PUB_KEY_CRED_CTAP1:
|
||||||
|
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 (memcmp(desc->credential.id.tag, tag, CREDENTIAL_TAG_SIZE) == 0);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -559,7 +682,7 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
|||||||
CTAP_makeCredential MC;
|
CTAP_makeCredential MC;
|
||||||
int ret;
|
int ret;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
uint8_t auth_data_buf[300];
|
uint8_t auth_data_buf[310];
|
||||||
CTAP_credentialDescriptor * excl_cred = (CTAP_credentialDescriptor *) auth_data_buf;
|
CTAP_credentialDescriptor * excl_cred = (CTAP_credentialDescriptor *) auth_data_buf;
|
||||||
uint8_t * sigbuf = auth_data_buf + 32;
|
uint8_t * sigbuf = auth_data_buf + 32;
|
||||||
uint8_t * sigder = auth_data_buf + 32 + 64;
|
uint8_t * sigder = auth_data_buf + 32 + 64;
|
||||||
@ -571,6 +694,14 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
|||||||
printf2(TAG_ERR,"error, parse_make_credential failed\n");
|
printf2(TAG_ERR,"error, parse_make_credential failed\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
if ((MC.paramsParsed & MC_requiredMask) != MC_requiredMask)
|
if ((MC.paramsParsed & MC_requiredMask) != MC_requiredMask)
|
||||||
{
|
{
|
||||||
printf2(TAG_ERR,"error, required parameter(s) for makeCredential are missing\n");
|
printf2(TAG_ERR,"error, required parameter(s) for makeCredential are missing\n");
|
||||||
@ -607,9 +738,7 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
|||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
|
|
||||||
printf1(TAG_GREEN, "checking credId: "); dump_hex1(TAG_GREEN, (uint8_t*) &excl_cred->credential.id, sizeof(CredentialId));
|
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))
|
if (ctap_authenticate_credential(&MC.rp, excl_cred))
|
||||||
{
|
{
|
||||||
printf1(TAG_MC, "Cred %d failed!\r\n",i);
|
printf1(TAG_MC, "Cred %d failed!\r\n",i);
|
||||||
@ -624,19 +753,6 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
|||||||
CborEncoder map;
|
CborEncoder map;
|
||||||
ret = cbor_encoder_create_map(encoder, &map, 3);
|
ret = cbor_encoder_create_map(encoder, &map, 3);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
int32_t auth_data_sz;
|
|
||||||
|
|
||||||
ret = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, sizeof(auth_data_buf),
|
|
||||||
&MC.user, MC.publicKeyCredentialType, MC.COSEAlgorithmIdentifier, &auth_data_sz, MC.rk);
|
|
||||||
|
|
||||||
check_retr(ret);
|
|
||||||
|
|
||||||
crypto_ecc256_load_attestation_key();
|
|
||||||
int sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_sz, MC.clientDataHash, auth_data_buf, sigbuf, sigder);
|
|
||||||
printf1(TAG_MC,"der sig [%d]: ", sigder_sz); dump_hex1(TAG_MC, sigder, sigder_sz);
|
|
||||||
|
|
||||||
ret = ctap_add_attest_statement(&map, sigder, sigder_sz);
|
|
||||||
check_retr(ret);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
ret = cbor_encode_int(&map,RESP_fmt);
|
ret = cbor_encode_int(&map,RESP_fmt);
|
||||||
@ -645,6 +761,39 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
|||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t auth_data_sz = sizeof(auth_data_buf);
|
||||||
|
|
||||||
|
ret = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, &auth_data_sz,
|
||||||
|
&MC.credInfo);
|
||||||
|
check_retr(ret);
|
||||||
|
|
||||||
|
{
|
||||||
|
unsigned int ext_encoder_buf_size = sizeof(auth_data_buf) - auth_data_sz;
|
||||||
|
uint8_t * ext_encoder_buf = auth_data_buf + auth_data_sz;
|
||||||
|
|
||||||
|
ret = ctap_make_extensions(&MC.extensions, ext_encoder_buf, &ext_encoder_buf_size);
|
||||||
|
check_retr(ret);
|
||||||
|
if (ext_encoder_buf_size)
|
||||||
|
{
|
||||||
|
((CTAP_authData *)auth_data_buf)->head.flags |= (1 << 7);
|
||||||
|
auth_data_sz += ext_encoder_buf_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ret = cbor_encode_int(&map,RESP_authData);
|
||||||
|
check_ret(ret);
|
||||||
|
ret = cbor_encode_byte_string(&map, auth_data_buf, auth_data_sz);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
crypto_ecc256_load_attestation_key();
|
||||||
|
int sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_sz, MC.clientDataHash, auth_data_buf, sigbuf, sigder);
|
||||||
|
printf1(TAG_MC,"der sig [%d]: ", sigder_sz); dump_hex1(TAG_MC, sigder, sigder_sz);
|
||||||
|
|
||||||
|
ret = ctap_add_attest_statement(&map, sigder, sigder_sz);
|
||||||
|
check_retr(ret);
|
||||||
|
|
||||||
ret = cbor_encoder_close_container(encoder, &map);
|
ret = cbor_encoder_close_container(encoder, &map);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
return CTAP1_ERR_SUCCESS;
|
return CTAP1_ERR_SUCCESS;
|
||||||
@ -672,6 +821,15 @@ static uint8_t ctap_add_credential_descriptor(CborEncoder * map, CTAP_credential
|
|||||||
ret = cbor_encoder_create_map(map, &desc, 2);
|
ret = cbor_encoder_create_map(map, &desc, 2);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
|
|
||||||
|
{
|
||||||
|
ret = cbor_encode_text_string(&desc, "id", 2);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
ret = cbor_encode_byte_string(&desc, (uint8_t*)&cred->credential.id,
|
||||||
|
get_credential_id_size(cred));
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ret = cbor_encode_text_string(&desc, "type", 4);
|
ret = cbor_encode_text_string(&desc, "type", 4);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
@ -679,13 +837,7 @@ static uint8_t ctap_add_credential_descriptor(CborEncoder * map, CTAP_credential
|
|||||||
ret = cbor_encode_text_string(&desc, "public-key", 10);
|
ret = cbor_encode_text_string(&desc, "public-key", 10);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
{
|
|
||||||
ret = cbor_encode_text_string(&desc, "id", 2);
|
|
||||||
check_ret(ret);
|
|
||||||
|
|
||||||
ret = cbor_encode_byte_string(&desc, (uint8_t*)&cred->credential.id, sizeof(CredentialId));
|
|
||||||
check_ret(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = cbor_encoder_close_container(map, &desc);
|
ret = cbor_encoder_close_container(map, &desc);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
@ -707,7 +859,6 @@ uint8_t ctap_add_user_entity(CborEncoder * map, CTAP_userEntity * user)
|
|||||||
ret = cbor_encoder_create_map(map, &entity, 1);
|
ret = cbor_encoder_create_map(map, &entity, 1);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
|
|
||||||
printf1(TAG_GREEN,"id_size: %d\r\n", user->id_size);
|
|
||||||
{
|
{
|
||||||
ret = cbor_encode_text_string(&entity, "id", 2);
|
ret = cbor_encode_text_string(&entity, "id", 2);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
@ -718,6 +869,13 @@ uint8_t ctap_add_user_entity(CborEncoder * map, CTAP_userEntity * user)
|
|||||||
|
|
||||||
if (dispname)
|
if (dispname)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
ret = cbor_encode_text_string(&entity, "icon", 4);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
ret = cbor_encode_text_stringz(&entity, (const char *)user->icon);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
ret = cbor_encode_text_string(&entity, "name", 4);
|
ret = cbor_encode_text_string(&entity, "name", 4);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
|
|
||||||
@ -730,13 +888,6 @@ uint8_t ctap_add_user_entity(CborEncoder * map, CTAP_userEntity * user)
|
|||||||
ret = cbor_encode_text_stringz(&entity, (const char *)user->displayName);
|
ret = cbor_encode_text_stringz(&entity, (const char *)user->displayName);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
|
|
||||||
ret = cbor_encode_text_string(&entity, "icon", 4);
|
|
||||||
check_ret(ret);
|
|
||||||
|
|
||||||
ret = cbor_encode_text_stringz(&entity, (const char *)user->icon);
|
|
||||||
check_ret(ret);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = cbor_encoder_close_container(map, &entity);
|
ret = cbor_encoder_close_container(map, &entity);
|
||||||
@ -852,6 +1003,7 @@ static void save_credential_list(CTAP_authDataHeader * head, uint8_t * clientDat
|
|||||||
memmove(getAssertionState.clientDataHash, clientDataHash, CLIENT_DATA_HASH_SIZE);
|
memmove(getAssertionState.clientDataHash, clientDataHash, CLIENT_DATA_HASH_SIZE);
|
||||||
memmove(&getAssertionState.authData, head, sizeof(CTAP_authDataHeader));
|
memmove(&getAssertionState.authData, head, sizeof(CTAP_authDataHeader));
|
||||||
memmove(getAssertionState.creds, creds, sizeof(CTAP_credentialDescriptor) * (count));
|
memmove(getAssertionState.creds, creds, sizeof(CTAP_credentialDescriptor) * (count));
|
||||||
|
|
||||||
}
|
}
|
||||||
getAssertionState.count = count;
|
getAssertionState.count = count;
|
||||||
printf1(TAG_GA,"saved %d credentials\n",count);
|
printf1(TAG_GA,"saved %d credentials\n",count);
|
||||||
@ -871,24 +1023,25 @@ static CTAP_credentialDescriptor * pop_credential()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// adds 2 to map, or 3 if add_user is true
|
// adds 2 to map, or 3 if add_user is true
|
||||||
uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cred, uint8_t * auth_data_buf, uint8_t * clientDataHash, int add_user)
|
uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cred, uint8_t * auth_data_buf, unsigned int auth_data_buf_sz, uint8_t * clientDataHash)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
uint8_t sigbuf[64];
|
uint8_t sigbuf[64];
|
||||||
uint8_t sigder[72];
|
uint8_t sigder[72];
|
||||||
int sigder_sz;
|
int sigder_sz;
|
||||||
|
|
||||||
if (add_user)
|
ret = ctap_add_credential_descriptor(map, cred); // 1
|
||||||
{
|
|
||||||
printf1(TAG_GREEN, "adding user details to output\r\n");
|
|
||||||
ret = ctap_add_user_entity(map, &cred->credential.user);
|
|
||||||
check_retr(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = ctap_add_credential_descriptor(map, cred);
|
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
|
|
||||||
crypto_ecc256_load_key((uint8_t*)&cred->credential.id, sizeof(CredentialId), NULL, 0);
|
{
|
||||||
|
ret = cbor_encode_int(map,RESP_authData); // 2
|
||||||
|
check_ret(ret);
|
||||||
|
ret = cbor_encode_byte_string(map, auth_data_buf, auth_data_buf_sz);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int cred_size = get_credential_id_size(cred);
|
||||||
|
crypto_ecc256_load_key((uint8_t*)&cred->credential.id, cred_size, NULL, 0);
|
||||||
|
|
||||||
#ifdef ENABLE_U2F_EXTENSIONS
|
#ifdef ENABLE_U2F_EXTENSIONS
|
||||||
if ( extend_fido2(&cred->credential.id, sigder) )
|
if ( extend_fido2(&cred->credential.id, sigder) )
|
||||||
@ -902,11 +1055,20 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ret = cbor_encode_int(map, RESP_signature);
|
ret = cbor_encode_int(map, RESP_signature); // 3
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
ret = cbor_encode_byte_string(map, sigder, sigder_sz);
|
ret = cbor_encode_byte_string(map, sigder, sigder_sz);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cred->credential.user.id_size)
|
||||||
|
{
|
||||||
|
printf1(TAG_GREEN, "adding user details to output\r\n");
|
||||||
|
ret = ctap_add_user_entity(map, &cred->credential.user); // 4
|
||||||
|
check_retr(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -916,7 +1078,6 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
|||||||
CborEncoder map;
|
CborEncoder map;
|
||||||
CTAP_authDataHeader authData;
|
CTAP_authDataHeader authData;
|
||||||
memmove(&authData, &getAssertionState.authData, sizeof(CTAP_authDataHeader));
|
memmove(&authData, &getAssertionState.authData, sizeof(CTAP_authDataHeader));
|
||||||
// CTAP_authDataHeader * authData = &getAssertionState.authData;
|
|
||||||
|
|
||||||
CTAP_credentialDescriptor * cred = pop_credential();
|
CTAP_credentialDescriptor * cred = pop_credential();
|
||||||
|
|
||||||
@ -926,9 +1087,8 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auth_data_update_count(&authData);
|
auth_data_update_count(&authData);
|
||||||
int add_user_info = cred->credential.user.id_size;
|
|
||||||
|
|
||||||
if (add_user_info)
|
if (cred->credential.user.id_size)
|
||||||
{
|
{
|
||||||
printf1(TAG_GREEN, "adding user info to assertion response\r\n");
|
printf1(TAG_GREEN, "adding user info to assertion response\r\n");
|
||||||
ret = cbor_encoder_create_map(encoder, &map, 4);
|
ret = cbor_encoder_create_map(encoder, &map, 4);
|
||||||
@ -940,14 +1100,6 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
|||||||
}
|
}
|
||||||
|
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
printf1(TAG_RED, "RPID hash: "); dump_hex1(TAG_RED, authData.rpIdHash, 32);
|
|
||||||
|
|
||||||
{
|
|
||||||
ret = cbor_encode_int(&map,RESP_authData);
|
|
||||||
check_ret(ret);
|
|
||||||
ret = cbor_encode_byte_string(&map, (uint8_t *)&authData, sizeof(CTAP_authDataHeader));
|
|
||||||
check_ret(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if only one account for this RP, null out the user details
|
// if only one account for this RP, null out the user details
|
||||||
if (!getAssertionState.user_verified)
|
if (!getAssertionState.user_verified)
|
||||||
@ -956,8 +1108,7 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
|||||||
memset(cred->credential.user.name, 0, USER_NAME_LIMIT);
|
memset(cred->credential.user.name, 0, USER_NAME_LIMIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = ctap_end_get_assertion(&map, cred, (uint8_t *)&authData, sizeof(CTAP_authDataHeader), getAssertionState.clientDataHash);
|
||||||
ret = ctap_end_get_assertion(&map, cred, (uint8_t *)&authData, getAssertionState.clientDataHash, add_user_info);
|
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
|
|
||||||
ret = cbor_encoder_close_container(encoder, &map);
|
ret = cbor_encoder_close_container(encoder, &map);
|
||||||
@ -969,7 +1120,8 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
|||||||
uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
||||||
{
|
{
|
||||||
CTAP_getAssertion GA;
|
CTAP_getAssertion GA;
|
||||||
uint8_t auth_data_buf[sizeof(CTAP_authDataHeader)];
|
|
||||||
|
uint8_t auth_data_buf[sizeof(CTAP_authDataHeader) + 80];
|
||||||
int ret = ctap_parse_get_assertion(&GA,request,length);
|
int ret = ctap_parse_get_assertion(&GA,request,length);
|
||||||
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
@ -978,19 +1130,23 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctap_is_pin_set() && GA.pinAuthPresent == 0)
|
if (GA.pinAuthEmpty)
|
||||||
{
|
{
|
||||||
printf2(TAG_ERR,"pinAuth is required\n");
|
if (!ctap_user_presence_test())
|
||||||
return CTAP2_ERR_PIN_REQUIRED;
|
{
|
||||||
|
return CTAP2_ERR_OPERATION_DENIED;
|
||||||
|
}
|
||||||
|
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET;
|
||||||
|
}
|
||||||
|
if (GA.pinAuthPresent)
|
||||||
|
{
|
||||||
|
ret = verify_pin_auth(GA.pinAuth, GA.clientDataHash);
|
||||||
|
check_retr(ret);
|
||||||
|
getAssertionState.user_verified = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (ctap_is_pin_set() || (GA.pinAuthPresent))
|
getAssertionState.user_verified = 0;
|
||||||
{
|
|
||||||
ret = verify_pin_auth(GA.pinAuth, GA.clientDataHash);
|
|
||||||
check_retr(ret);
|
|
||||||
getAssertionState.user_verified = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GA.rp.size || !GA.clientDataHashPresent)
|
if (!GA.rp.size || !GA.clientDataHashPresent)
|
||||||
@ -1004,62 +1160,28 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
|||||||
printf1(TAG_GA, "ALLOW_LIST has %d creds\n", GA.credLen);
|
printf1(TAG_GA, "ALLOW_LIST has %d creds\n", GA.credLen);
|
||||||
int validCredCount = ctap_filter_invalid_credentials(&GA);
|
int validCredCount = ctap_filter_invalid_credentials(&GA);
|
||||||
|
|
||||||
int add_user_info = GA.creds[validCredCount - 1].credential.user.id_size;
|
if (validCredCount == 0)
|
||||||
if (validCredCount > 1)
|
|
||||||
{
|
|
||||||
map_size += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (add_user_info)
|
|
||||||
{
|
|
||||||
map_size += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = cbor_encoder_create_map(encoder, &map, map_size);
|
|
||||||
check_ret(ret);
|
|
||||||
|
|
||||||
#ifdef ENABLE_U2F_EXTENSIONS
|
|
||||||
if ( is_extension_request((uint8_t*)&GA.creds[validCredCount - 1].credential.id, sizeof(CredentialId)) )
|
|
||||||
{
|
|
||||||
ret = cbor_encode_int(&map,RESP_authData);
|
|
||||||
check_ret(ret);
|
|
||||||
memset(auth_data_buf,0,sizeof(auth_data_buf));
|
|
||||||
ret = cbor_encode_byte_string(&map, auth_data_buf, sizeof(auth_data_buf));
|
|
||||||
check_ret(ret);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, sizeof(auth_data_buf), NULL, 0,0,NULL, 0);
|
|
||||||
check_retr(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*for (int j = 0; j < GA.credLen; j++)*/
|
|
||||||
/*{*/
|
|
||||||
/*printf1(TAG_GA,"CRED ID (# %d): ", GA.creds[j].credential.enc.count);*/
|
|
||||||
/*dump_hex1(TAG_GA, (uint8_t*)&GA.creds[j].credential, sizeof(struct Credential));*/
|
|
||||||
/*if (ctap_authenticate_credential(&GA.rp, &GA.creds[j])) // warning encryption will break this*/
|
|
||||||
/*{*/
|
|
||||||
/*printf1(TAG_GA," Authenticated.\n");*/
|
|
||||||
/*}*/
|
|
||||||
/*else*/
|
|
||||||
/*{*/
|
|
||||||
/*printf1(TAG_GA," NOT authentic.\n");*/
|
|
||||||
/*}*/
|
|
||||||
/*}*/
|
|
||||||
|
|
||||||
// Decrypt here
|
|
||||||
|
|
||||||
//
|
|
||||||
if (validCredCount > 0)
|
|
||||||
{
|
|
||||||
save_credential_list((CTAP_authDataHeader*)auth_data_buf, GA.clientDataHash, GA.creds, validCredCount-1); // skip last one
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
printf2(TAG_ERR,"Error, no authentic credential\n");
|
printf2(TAG_ERR,"Error, no authentic credential\n");
|
||||||
return CTAP2_ERR_NO_CREDENTIALS;
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cbor_encoder_create_map(encoder, &map, map_size);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
// if only one account for this RP, null out the user details
|
// if only one account for this RP, null out the user details
|
||||||
if (validCredCount < 2 || !getAssertionState.user_verified)
|
if (validCredCount < 2 || !getAssertionState.user_verified)
|
||||||
@ -1075,19 +1197,62 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
|||||||
printf1(TAG_GA,"CRED ID (# %d)\n", GA.creds[j].credential.id.count);
|
printf1(TAG_GA,"CRED ID (# %d)\n", GA.creds[j].credential.id.count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CTAP_credentialDescriptor * cred = &GA.creds[validCredCount - 1];
|
||||||
|
|
||||||
|
GA.extensions.hmac_secret.credential = &cred->credential;
|
||||||
|
|
||||||
|
uint32_t auth_data_buf_sz = sizeof(auth_data_buf);
|
||||||
|
|
||||||
|
#ifdef ENABLE_U2F_EXTENSIONS
|
||||||
|
if ( is_extension_request((uint8_t*)&GA.creds[validCredCount - 1].credential.id, sizeof(CredentialId)) )
|
||||||
|
{
|
||||||
|
auth_data_buf_sz = sizeof(CTAP_authDataHeader);
|
||||||
|
|
||||||
|
crypto_sha256_init();
|
||||||
|
crypto_sha256_update(GA.rp.id, GA.rp.size);
|
||||||
|
crypto_sha256_final(((CTAP_authData *)auth_data_buf)->head.rpIdHash);
|
||||||
|
|
||||||
|
((CTAP_authData *)auth_data_buf)->head.flags = (1 << 0);
|
||||||
|
((CTAP_authData *)auth_data_buf)->head.flags |= (1 << 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
{
|
||||||
|
unsigned int ext_encoder_buf_size = sizeof(auth_data_buf) - auth_data_buf_sz;
|
||||||
|
uint8_t * ext_encoder_buf = auth_data_buf + auth_data_buf_sz;
|
||||||
|
|
||||||
|
ret = ctap_make_extensions(&GA.extensions, ext_encoder_buf, &ext_encoder_buf_size);
|
||||||
|
check_retr(ret);
|
||||||
|
if (ext_encoder_buf_size)
|
||||||
|
{
|
||||||
|
((CTAP_authData *)auth_data_buf)->head.flags |= (1 << 7);
|
||||||
|
auth_data_buf_sz += ext_encoder_buf_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
save_credential_list((CTAP_authDataHeader*)auth_data_buf, GA.clientDataHash, GA.creds, validCredCount-1); // skip last one
|
||||||
|
|
||||||
|
ret = ctap_end_get_assertion(&map, cred, auth_data_buf, auth_data_buf_sz, GA.clientDataHash); // 1,2,3,4
|
||||||
|
check_retr(ret);
|
||||||
|
|
||||||
if (validCredCount > 1)
|
if (validCredCount > 1)
|
||||||
{
|
{
|
||||||
ret = cbor_encode_int(&map, RESP_numberOfCredentials);
|
ret = cbor_encode_int(&map, RESP_numberOfCredentials); // 5
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
ret = cbor_encode_int(&map, validCredCount);
|
ret = cbor_encode_int(&map, validCredCount);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
CTAP_credentialDescriptor * cred = &GA.creds[validCredCount - 1];
|
|
||||||
|
|
||||||
ret = ctap_end_get_assertion(&map, cred, auth_data_buf, GA.clientDataHash, add_user_info);
|
|
||||||
check_retr(ret);
|
|
||||||
|
|
||||||
ret = cbor_encoder_close_container(encoder, &map);
|
ret = cbor_encoder_close_container(encoder, &map);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
|
|
||||||
@ -1305,7 +1470,7 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length)
|
|||||||
|
|
||||||
ret = cbor_encode_int(&map, RESP_keyAgreement);
|
ret = cbor_encode_int(&map, RESP_keyAgreement);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
ret = ctap_add_cose_key(&map, KEY_AGREEMENT_PUB, KEY_AGREEMENT_PUB+32, PUB_KEY_CRED_PUB_KEY, COSE_ALG_ES256);
|
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);
|
check_retr(ret);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -1406,7 +1571,6 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
|
|||||||
pkt_raw++;
|
pkt_raw++;
|
||||||
length--;
|
length--;
|
||||||
|
|
||||||
|
|
||||||
uint8_t * buf = resp->data;
|
uint8_t * buf = resp->data;
|
||||||
|
|
||||||
cbor_encoder_init(&encoder, buf, resp->data_size, 0);
|
cbor_encoder_init(&encoder, buf, resp->data_size, 0);
|
||||||
@ -1501,7 +1665,6 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf2(TAG_ERR, "unwanted GET_NEXT_ASSERTION. lastcmd == 0x%02x\n", getAssertionState.lastcmd);
|
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;
|
status = CTAP2_ERR_NOT_ALLOWED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
100
fido2/ctap.h
100
fido2/ctap.h
@ -54,6 +54,13 @@
|
|||||||
#define CP_getKeyAgreement 0x07
|
#define CP_getKeyAgreement 0x07
|
||||||
#define CP_getRetries 0x08
|
#define CP_getRetries 0x08
|
||||||
|
|
||||||
|
#define EXT_HMAC_SECRET_COSE_KEY 0x01
|
||||||
|
#define EXT_HMAC_SECRET_SALT_ENC 0x02
|
||||||
|
#define EXT_HMAC_SECRET_SALT_AUTH 0x03
|
||||||
|
|
||||||
|
#define EXT_HMAC_SECRET_REQUESTED 0x01
|
||||||
|
#define EXT_HMAC_SECRET_PARSED 0x02
|
||||||
|
|
||||||
#define RESP_versions 0x1
|
#define RESP_versions 0x1
|
||||||
#define RESP_extensions 0x2
|
#define RESP_extensions 0x2
|
||||||
#define RESP_aaguid 0x3
|
#define RESP_aaguid 0x3
|
||||||
@ -105,6 +112,8 @@
|
|||||||
#define CREDENTIAL_ENC_SIZE 176 // pad to multiple of 16 bytes
|
#define CREDENTIAL_ENC_SIZE 176 // pad to multiple of 16 bytes
|
||||||
|
|
||||||
#define PUB_KEY_CRED_PUB_KEY 0x01
|
#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 PUB_KEY_CRED_UNKNOWN 0x3F
|
||||||
|
|
||||||
#define CREDENTIAL_IS_SUPPORTED 1
|
#define CREDENTIAL_IS_SUPPORTED 1
|
||||||
@ -142,9 +151,13 @@ struct Credential {
|
|||||||
CredentialId id;
|
CredentialId id;
|
||||||
CTAP_userEntity user;
|
CTAP_userEntity user;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct Credential CTAP_residentKey;
|
typedef struct Credential CTAP_residentKey;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t type;
|
||||||
|
struct Credential credential;
|
||||||
|
} CTAP_credentialDescriptor;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -181,34 +194,67 @@ struct rpId
|
|||||||
uint8_t name[RP_NAME_LIMIT];
|
uint8_t name[RP_NAME_LIMIT];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
struct{
|
||||||
|
uint8_t x[32];
|
||||||
|
uint8_t y[32];
|
||||||
|
} pubkey;
|
||||||
|
|
||||||
|
int kty;
|
||||||
|
int crv;
|
||||||
|
} COSE_key;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t saltLen;
|
||||||
|
uint8_t saltEnc[64];
|
||||||
|
uint8_t saltAuth[32];
|
||||||
|
COSE_key keyAgreement;
|
||||||
|
struct Credential * credential;
|
||||||
|
} CTAP_hmac_secret;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t hmac_secret_present;
|
||||||
|
CTAP_hmac_secret hmac_secret;
|
||||||
|
} CTAP_extensions;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
CTAP_userEntity user;
|
||||||
|
uint8_t publicKeyCredentialType;
|
||||||
|
int32_t COSEAlgorithmIdentifier;
|
||||||
|
uint8_t rk;
|
||||||
|
} CTAP_credInfo;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32_t paramsParsed;
|
uint32_t paramsParsed;
|
||||||
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
|
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
|
||||||
struct rpId rp;
|
struct rpId rp;
|
||||||
CTAP_userEntity user;
|
|
||||||
|
|
||||||
uint8_t publicKeyCredentialType;
|
CTAP_credInfo credInfo;
|
||||||
int32_t COSEAlgorithmIdentifier;
|
|
||||||
|
|
||||||
CborValue excludeList;
|
CborValue excludeList;
|
||||||
size_t excludeListSize;
|
size_t excludeListSize;
|
||||||
|
|
||||||
uint8_t rk;
|
|
||||||
uint8_t uv;
|
uint8_t uv;
|
||||||
uint8_t up;
|
uint8_t up;
|
||||||
|
|
||||||
uint8_t pinAuth[16];
|
uint8_t pinAuth[16];
|
||||||
uint8_t pinAuthPresent;
|
uint8_t pinAuthPresent;
|
||||||
|
// pinAuthEmpty is true iff an empty bytestring was provided as pinAuth.
|
||||||
|
// This is exclusive with |pinAuthPresent|. It exists because an empty
|
||||||
|
// pinAuth is a special signal to block for touch. See
|
||||||
|
// https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorMakeCredential
|
||||||
|
uint8_t pinAuthEmpty;
|
||||||
int pinProtocol;
|
int pinProtocol;
|
||||||
|
CTAP_extensions extensions;
|
||||||
|
|
||||||
} CTAP_makeCredential;
|
} CTAP_makeCredential;
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint8_t type;
|
|
||||||
struct Credential credential;
|
|
||||||
} CTAP_credentialDescriptor;
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -226,26 +272,25 @@ typedef struct
|
|||||||
|
|
||||||
uint8_t pinAuth[16];
|
uint8_t pinAuth[16];
|
||||||
uint8_t pinAuthPresent;
|
uint8_t pinAuthPresent;
|
||||||
|
// pinAuthEmpty is true iff an empty bytestring was provided as pinAuth.
|
||||||
|
// This is exclusive with |pinAuthPresent|. It exists because an empty
|
||||||
|
// pinAuth is a special signal to block for touch. See
|
||||||
|
// https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorGetAssertion
|
||||||
|
uint8_t pinAuthEmpty;
|
||||||
int pinProtocol;
|
int pinProtocol;
|
||||||
|
|
||||||
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
|
CTAP_credentialDescriptor * creds;
|
||||||
uint8_t allowListPresent;
|
uint8_t allowListPresent;
|
||||||
|
|
||||||
|
CTAP_extensions extensions;
|
||||||
|
|
||||||
} CTAP_getAssertion;
|
} CTAP_getAssertion;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int pinProtocol;
|
int pinProtocol;
|
||||||
int subCommand;
|
int subCommand;
|
||||||
struct
|
COSE_key keyAgreement;
|
||||||
{
|
|
||||||
struct{
|
|
||||||
uint8_t x[32];
|
|
||||||
uint8_t y[32];
|
|
||||||
} pubkey;
|
|
||||||
|
|
||||||
int kty;
|
|
||||||
int crv;
|
|
||||||
} keyAgreement;
|
|
||||||
uint8_t keyAgreementPresent;
|
uint8_t keyAgreementPresent;
|
||||||
uint8_t pinAuth[16];
|
uint8_t pinAuth[16];
|
||||||
uint8_t pinAuthPresent;
|
uint8_t pinAuthPresent;
|
||||||
@ -258,6 +303,19 @@ typedef struct
|
|||||||
} CTAP_clientPin;
|
} 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);
|
void ctap_response_init(CTAP_RESPONSE * resp);
|
||||||
|
|
||||||
uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp);
|
uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp);
|
||||||
|
@ -9,12 +9,14 @@
|
|||||||
#include "cbor.h"
|
#include "cbor.h"
|
||||||
|
|
||||||
#include "ctap.h"
|
#include "ctap.h"
|
||||||
|
#include "u2f.h"
|
||||||
#include "ctap_parse.h"
|
#include "ctap_parse.h"
|
||||||
#include "ctap_errors.h"
|
#include "ctap_errors.h"
|
||||||
#include "cose_key.h"
|
#include "cose_key.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
extern struct _getAssertionState getAssertionState;
|
||||||
|
|
||||||
void _check_ret(CborError ret, int line, const char * filename)
|
void _check_ret(CborError ret, int line, const char * filename)
|
||||||
{
|
{
|
||||||
@ -128,14 +130,13 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
sz = USER_ID_MAX_SIZE;
|
sz = USER_ID_MAX_SIZE;
|
||||||
ret = cbor_value_copy_byte_string(&map, MC->user.id, &sz, NULL);
|
ret = cbor_value_copy_byte_string(&map, MC->credInfo.user.id, &sz, NULL);
|
||||||
if (ret == CborErrorOutOfMemory)
|
if (ret == CborErrorOutOfMemory)
|
||||||
{
|
{
|
||||||
printf2(TAG_ERR,"Error, USER_ID is too large\n");
|
printf2(TAG_ERR,"Error, USER_ID is too large\n");
|
||||||
return CTAP2_ERR_LIMIT_EXCEEDED;
|
return CTAP2_ERR_LIMIT_EXCEEDED;
|
||||||
}
|
}
|
||||||
MC->user.id_size = sz;
|
MC->credInfo.user.id_size = sz;
|
||||||
printf1(TAG_GREEN,"parsed id_size: %d\r\n", MC->user.id_size);
|
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
else if (strcmp((const char *)key, "name") == 0)
|
else if (strcmp((const char *)key, "name") == 0)
|
||||||
@ -146,12 +147,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
|
|||||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
}
|
}
|
||||||
sz = USER_NAME_LIMIT;
|
sz = USER_NAME_LIMIT;
|
||||||
ret = cbor_value_copy_text_string(&map, (char *)MC->user.name, &sz, NULL);
|
ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.name, &sz, NULL);
|
||||||
if (ret != CborErrorOutOfMemory)
|
if (ret != CborErrorOutOfMemory)
|
||||||
{ // Just truncate the name it's okay
|
{ // Just truncate the name it's okay
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
MC->user.name[USER_NAME_LIMIT - 1] = 0;
|
MC->credInfo.user.name[USER_NAME_LIMIT - 1] = 0;
|
||||||
}
|
}
|
||||||
else if (strcmp((const char *)key, "displayName") == 0)
|
else if (strcmp((const char *)key, "displayName") == 0)
|
||||||
{
|
{
|
||||||
@ -161,12 +162,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
|
|||||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
}
|
}
|
||||||
sz = DISPLAY_NAME_LIMIT;
|
sz = DISPLAY_NAME_LIMIT;
|
||||||
ret = cbor_value_copy_text_string(&map, (char *)MC->user.displayName, &sz, NULL);
|
ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.displayName, &sz, NULL);
|
||||||
if (ret != CborErrorOutOfMemory)
|
if (ret != CborErrorOutOfMemory)
|
||||||
{ // Just truncate the name it's okay
|
{ // Just truncate the name it's okay
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
MC->user.displayName[DISPLAY_NAME_LIMIT - 1] = 0;
|
MC->credInfo.user.displayName[DISPLAY_NAME_LIMIT - 1] = 0;
|
||||||
}
|
}
|
||||||
else if (strcmp((const char *)key, "icon") == 0)
|
else if (strcmp((const char *)key, "icon") == 0)
|
||||||
{
|
{
|
||||||
@ -176,12 +177,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
|
|||||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
}
|
}
|
||||||
sz = ICON_LIMIT;
|
sz = ICON_LIMIT;
|
||||||
ret = cbor_value_copy_text_string(&map, (char *)MC->user.icon, &sz, NULL);
|
ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.icon, &sz, NULL);
|
||||||
if (ret != CborErrorOutOfMemory)
|
if (ret != CborErrorOutOfMemory)
|
||||||
{ // Just truncate the name it's okay
|
{ // Just truncate the name it's okay
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
MC->user.icon[ICON_LIMIT - 1] = 0;
|
MC->credInfo.user.icon[ICON_LIMIT - 1] = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -305,8 +306,8 @@ uint8_t parse_pub_key_cred_params(CTAP_makeCredential * MC, CborValue * val)
|
|||||||
{
|
{
|
||||||
if (pub_key_cred_param_supported(cred_type, alg_type) == CREDENTIAL_IS_SUPPORTED)
|
if (pub_key_cred_param_supported(cred_type, alg_type) == CREDENTIAL_IS_SUPPORTED)
|
||||||
{
|
{
|
||||||
MC->publicKeyCredentialType = cred_type;
|
MC->credInfo.publicKeyCredentialType = cred_type;
|
||||||
MC->COSEAlgorithmIdentifier = alg_type;
|
MC->credInfo.COSEAlgorithmIdentifier = alg_type;
|
||||||
MC->paramsParsed |= PARAM_pubKeyCredParams;
|
MC->paramsParsed |= PARAM_pubKeyCredParams;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -556,6 +557,154 @@ uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t ctap_parse_hmac_secret(CborValue * val, CTAP_hmac_secret * hs)
|
||||||
|
{
|
||||||
|
size_t map_length;
|
||||||
|
size_t salt_len;
|
||||||
|
uint8_t parsed_count = 0;
|
||||||
|
int key;
|
||||||
|
int ret;
|
||||||
|
unsigned int i;
|
||||||
|
CborValue map;
|
||||||
|
|
||||||
|
if (cbor_value_get_type(val) != CborMapType)
|
||||||
|
{
|
||||||
|
printf2(TAG_ERR,"error, wrong type\n");
|
||||||
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cbor_value_enter_container(val,&map);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
ret = cbor_value_get_map_length(val, &map_length);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
for (i = 0; i < map_length; i++)
|
||||||
|
{
|
||||||
|
if (cbor_value_get_type(&map) != CborIntegerType)
|
||||||
|
{
|
||||||
|
printf2(TAG_ERR,"Error, expecting CborIntegerTypefor hmac-secret map key, got %s\n", cbor_value_get_type_string(&map));
|
||||||
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
|
}
|
||||||
|
ret = cbor_value_get_int(&map, &key);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
ret = cbor_value_advance(&map);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
switch(key)
|
||||||
|
{
|
||||||
|
case EXT_HMAC_SECRET_COSE_KEY:
|
||||||
|
ret = parse_cose_key(&map, &hs->keyAgreement);
|
||||||
|
check_retr(ret);
|
||||||
|
parsed_count++;
|
||||||
|
break;
|
||||||
|
case EXT_HMAC_SECRET_SALT_ENC:
|
||||||
|
salt_len = 64;
|
||||||
|
ret = cbor_value_copy_byte_string(&map, hs->saltEnc, &salt_len, NULL);
|
||||||
|
if ((salt_len != 32 && salt_len != 64) || ret == CborErrorOutOfMemory)
|
||||||
|
{
|
||||||
|
return CTAP1_ERR_INVALID_LENGTH;
|
||||||
|
}
|
||||||
|
check_ret(ret);
|
||||||
|
hs->saltLen = salt_len;
|
||||||
|
parsed_count++;
|
||||||
|
break;
|
||||||
|
case EXT_HMAC_SECRET_SALT_AUTH:
|
||||||
|
salt_len = 32;
|
||||||
|
ret = cbor_value_copy_byte_string(&map, hs->saltAuth, &salt_len, NULL);
|
||||||
|
check_ret(ret);
|
||||||
|
parsed_count++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cbor_value_advance(&map);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed_count != 3)
|
||||||
|
{
|
||||||
|
printf2(TAG_ERR, "ctap_parse_hmac_secret missing parameter. Got %d.\r\n", parsed_count);
|
||||||
|
return CTAP2_ERR_MISSING_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext)
|
||||||
|
{
|
||||||
|
CborValue map;
|
||||||
|
size_t sz, map_length;
|
||||||
|
char key[16];
|
||||||
|
int ret;
|
||||||
|
unsigned int i;
|
||||||
|
bool b;
|
||||||
|
|
||||||
|
if (cbor_value_get_type(val) != CborMapType)
|
||||||
|
{
|
||||||
|
printf2(TAG_ERR,"error, wrong type\n");
|
||||||
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cbor_value_enter_container(val, &map);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
ret = cbor_value_get_map_length(val, &map_length);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
for (i = 0; i < map_length; i++)
|
||||||
|
{
|
||||||
|
if (cbor_value_get_type(&map) != CborTextStringType)
|
||||||
|
{
|
||||||
|
printf2(TAG_ERR,"Error, expecting text string type for options map key, got %s\n", cbor_value_get_type_string(&map));
|
||||||
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
|
}
|
||||||
|
sz = sizeof(key);
|
||||||
|
ret = cbor_value_copy_text_string(&map, key, &sz, NULL);
|
||||||
|
|
||||||
|
if (ret == CborErrorOutOfMemory)
|
||||||
|
{
|
||||||
|
printf2(TAG_ERR,"Error, rp map key is too large. Ignoring.\n");
|
||||||
|
cbor_value_advance(&map);
|
||||||
|
cbor_value_advance(&map);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
check_ret(ret);
|
||||||
|
key[sizeof(key) - 1] = 0;
|
||||||
|
|
||||||
|
ret = cbor_value_advance(&map);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
|
|
||||||
|
if (strncmp(key, "hmac-secret",11) == 0)
|
||||||
|
{
|
||||||
|
if (cbor_value_get_type(&map) == CborBooleanType)
|
||||||
|
{
|
||||||
|
ret = cbor_value_get_boolean(&map, &b);
|
||||||
|
check_ret(ret);
|
||||||
|
if (b) ext->hmac_secret_present = EXT_HMAC_SECRET_REQUESTED;
|
||||||
|
printf1(TAG_CTAP, "set hmac_secret_present to %d\r\n", b);
|
||||||
|
}
|
||||||
|
else if (cbor_value_get_type(&map) == CborMapType)
|
||||||
|
{
|
||||||
|
ret = ctap_parse_hmac_secret(&map, &ext->hmac_secret);
|
||||||
|
check_retr(ret);
|
||||||
|
ext->hmac_secret_present = EXT_HMAC_SECRET_PARSED;
|
||||||
|
printf1(TAG_CTAP, "parsed hmac_secret request\r\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf1(TAG_RED, "warning: hmac_secret request ignored for being wrong type\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cbor_value_advance(&map);
|
||||||
|
check_ret(ret);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length)
|
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -631,8 +780,8 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
|
|||||||
|
|
||||||
ret = parse_user(MC, &map);
|
ret = parse_user(MC, &map);
|
||||||
|
|
||||||
printf1(TAG_MC," ID: "); dump_hex1(TAG_MC, MC->user.id, MC->user.id_size);
|
printf1(TAG_MC," ID: "); dump_hex1(TAG_MC, MC->credInfo.user.id, MC->credInfo.user.id_size);
|
||||||
printf1(TAG_MC," name: %s\n", MC->user.name);
|
printf1(TAG_MC," name: %s\n", MC->credInfo.user.name);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case MC_pubKeyCredParams:
|
case MC_pubKeyCredParams:
|
||||||
@ -640,8 +789,8 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
|
|||||||
|
|
||||||
ret = parse_pub_key_cred_params(MC, &map);
|
ret = parse_pub_key_cred_params(MC, &map);
|
||||||
|
|
||||||
printf1(TAG_MC," cred_type: 0x%02x\n", MC->publicKeyCredentialType);
|
printf1(TAG_MC," cred_type: 0x%02x\n", MC->credInfo.publicKeyCredentialType);
|
||||||
printf1(TAG_MC," alg_type: %d\n", MC->COSEAlgorithmIdentifier);
|
printf1(TAG_MC," alg_type: %d\n", MC->credInfo.COSEAlgorithmIdentifier);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case MC_excludeList:
|
case MC_excludeList:
|
||||||
@ -665,21 +814,31 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
|
|||||||
{
|
{
|
||||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||||
}
|
}
|
||||||
|
ret = ctap_parse_extensions(&map, &MC->extensions);
|
||||||
|
check_retr(ret);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MC_options:
|
case MC_options:
|
||||||
printf1(TAG_MC,"CTAP_options\n");
|
printf1(TAG_MC,"CTAP_options\n");
|
||||||
ret = parse_options(&map, &MC->rk, &MC->uv, &MC->up);
|
ret = parse_options(&map, &MC->credInfo.rk, &MC->uv, &MC->up);
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
break;
|
break;
|
||||||
case MC_pinAuth:
|
case MC_pinAuth: {
|
||||||
printf1(TAG_MC,"CTAP_pinAuth\n");
|
printf1(TAG_MC,"CTAP_pinAuth\n");
|
||||||
|
|
||||||
|
size_t pinSize;
|
||||||
|
if (cbor_value_get_type(&map) == CborByteStringType &&
|
||||||
|
cbor_value_get_string_length(&map, &pinSize) == CborNoError &&
|
||||||
|
pinSize == 0)
|
||||||
|
{
|
||||||
|
MC->pinAuthEmpty = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
ret = parse_fixed_byte_string(&map, MC->pinAuth, 16);
|
ret = parse_fixed_byte_string(&map, MC->pinAuth, 16);
|
||||||
if (CTAP1_ERR_INVALID_LENGTH != ret) // damn microsoft
|
if (CTAP1_ERR_INVALID_LENGTH != ret) // damn microsoft
|
||||||
{
|
{
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -687,6 +846,7 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
|
|||||||
}
|
}
|
||||||
MC->pinAuthPresent = 1;
|
MC->pinAuthPresent = 1;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case MC_pinProtocol:
|
case MC_pinProtocol:
|
||||||
printf1(TAG_MC,"CTAP_pinProtocol\n");
|
printf1(TAG_MC,"CTAP_pinProtocol\n");
|
||||||
if (cbor_value_get_type(&map) == CborIntegerType)
|
if (cbor_value_get_type(&map) == CborIntegerType)
|
||||||
@ -723,6 +883,8 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
|
|||||||
size_t buflen;
|
size_t buflen;
|
||||||
char type[12];
|
char type[12];
|
||||||
CborValue val;
|
CborValue val;
|
||||||
|
cred->type = 0;
|
||||||
|
|
||||||
if (cbor_value_get_type(arr) != CborMapType)
|
if (cbor_value_get_type(arr) != CborMapType)
|
||||||
{
|
{
|
||||||
printf2(TAG_ERR,"Error, CborMapType expected in credential\n");
|
printf2(TAG_ERR,"Error, CborMapType expected in credential\n");
|
||||||
@ -739,12 +901,22 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
|
|||||||
}
|
}
|
||||||
|
|
||||||
buflen = sizeof(CredentialId);
|
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 != sizeof(CredentialId))
|
|
||||||
|
if (buflen == U2F_KEY_HANDLE_SIZE)
|
||||||
{
|
{
|
||||||
printf2(TAG_ERR,"Ignoring credential is incorrect length\n");
|
printf2(TAG_PARSE,"CTAP1 credential\n");
|
||||||
//return CTAP2_ERR_CBOR_UNEXPECTED_TYPE; // maybe just skip it instead of fail?
|
cred->type = PUB_KEY_CRED_CTAP1;
|
||||||
}
|
}
|
||||||
|
else if (buflen != sizeof(CredentialId))
|
||||||
|
{
|
||||||
|
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);
|
ret = cbor_value_map_find_value(arr, "type", &val);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
@ -756,11 +928,15 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
|
|||||||
}
|
}
|
||||||
|
|
||||||
buflen = sizeof(type);
|
buflen = sizeof(type);
|
||||||
cbor_value_copy_text_string(&val, type, &buflen, NULL);
|
ret = cbor_value_copy_text_string(&val, type, &buflen, NULL);
|
||||||
|
check_ret(ret);
|
||||||
|
|
||||||
if (strncmp(type, "public-key",11) == 0)
|
if (strncmp(type, "public-key",11) == 0)
|
||||||
{
|
{
|
||||||
cred->type = PUB_KEY_CRED_PUB_KEY;
|
if (0 == cred->type)
|
||||||
|
{
|
||||||
|
cred->type = PUB_KEY_CRED_PUB_KEY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -825,6 +1001,8 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
|
|||||||
CborValue it,map;
|
CborValue it,map;
|
||||||
|
|
||||||
memset(GA, 0, sizeof(CTAP_getAssertion));
|
memset(GA, 0, sizeof(CTAP_getAssertion));
|
||||||
|
GA->creds = getAssertionState.creds; // Save stack memory
|
||||||
|
|
||||||
ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
|
ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
|
|
||||||
@ -886,6 +1064,8 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
|
|||||||
break;
|
break;
|
||||||
case GA_extensions:
|
case GA_extensions:
|
||||||
printf1(TAG_GA,"GA_extensions\n");
|
printf1(TAG_GA,"GA_extensions\n");
|
||||||
|
ret = ctap_parse_extensions(&map, &GA->extensions);
|
||||||
|
check_retr(ret);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GA_options:
|
case GA_options:
|
||||||
@ -893,9 +1073,18 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
|
|||||||
ret = parse_options(&map, &GA->rk, &GA->uv, &GA->up);
|
ret = parse_options(&map, &GA->rk, &GA->uv, &GA->up);
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
break;
|
break;
|
||||||
case GA_pinAuth:
|
case GA_pinAuth: {
|
||||||
printf1(TAG_GA,"CTAP_pinAuth\n");
|
printf1(TAG_GA,"CTAP_pinAuth\n");
|
||||||
|
|
||||||
|
size_t pinSize;
|
||||||
|
if (cbor_value_get_type(&map) == CborByteStringType &&
|
||||||
|
cbor_value_get_string_length(&map, &pinSize) == CborNoError &&
|
||||||
|
pinSize == 0)
|
||||||
|
{
|
||||||
|
GA->pinAuthEmpty = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
ret = parse_fixed_byte_string(&map, GA->pinAuth, 16);
|
ret = parse_fixed_byte_string(&map, GA->pinAuth, 16);
|
||||||
if (CTAP1_ERR_INVALID_LENGTH != ret) // damn microsoft
|
if (CTAP1_ERR_INVALID_LENGTH != ret) // damn microsoft
|
||||||
{
|
{
|
||||||
@ -911,6 +1100,7 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
|
|||||||
GA->pinAuthPresent = 1;
|
GA->pinAuthPresent = 1;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case GA_pinProtocol:
|
case GA_pinProtocol:
|
||||||
printf1(TAG_GA,"CTAP_pinProtocol\n");
|
printf1(TAG_GA,"CTAP_pinProtocol\n");
|
||||||
if (cbor_value_get_type(&map) == CborIntegerType)
|
if (cbor_value_get_type(&map) == CborIntegerType)
|
||||||
@ -940,15 +1130,15 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int * crv)
|
uint8_t parse_cose_key(CborValue * it, COSE_key * cose)
|
||||||
{
|
{
|
||||||
CborValue map;
|
CborValue map;
|
||||||
size_t map_length;
|
size_t map_length;
|
||||||
int ret,key;
|
int ret,key;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int xkey = 0,ykey = 0;
|
int xkey = 0,ykey = 0;
|
||||||
*kty = 0;
|
cose->kty = 0;
|
||||||
*crv = 0;
|
cose->crv = 0;
|
||||||
|
|
||||||
|
|
||||||
CborType type = cbor_value_get_type(it);
|
CborType type = cbor_value_get_type(it);
|
||||||
@ -986,7 +1176,7 @@ uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int
|
|||||||
printf1(TAG_PARSE,"COSE_KEY_LABEL_KTY\n");
|
printf1(TAG_PARSE,"COSE_KEY_LABEL_KTY\n");
|
||||||
if (cbor_value_get_type(&map) == CborIntegerType)
|
if (cbor_value_get_type(&map) == CborIntegerType)
|
||||||
{
|
{
|
||||||
ret = cbor_value_get_int_checked(&map, kty);
|
ret = cbor_value_get_int_checked(&map, &cose->kty);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1001,7 +1191,7 @@ uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int
|
|||||||
printf1(TAG_PARSE,"COSE_KEY_LABEL_CRV\n");
|
printf1(TAG_PARSE,"COSE_KEY_LABEL_CRV\n");
|
||||||
if (cbor_value_get_type(&map) == CborIntegerType)
|
if (cbor_value_get_type(&map) == CborIntegerType)
|
||||||
{
|
{
|
||||||
ret = cbor_value_get_int_checked(&map, crv);
|
ret = cbor_value_get_int_checked(&map, &cose->crv);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1011,14 +1201,14 @@ uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int
|
|||||||
break;
|
break;
|
||||||
case COSE_KEY_LABEL_X:
|
case COSE_KEY_LABEL_X:
|
||||||
printf1(TAG_PARSE,"COSE_KEY_LABEL_X\n");
|
printf1(TAG_PARSE,"COSE_KEY_LABEL_X\n");
|
||||||
ret = parse_fixed_byte_string(&map, x, 32);
|
ret = parse_fixed_byte_string(&map, cose->pubkey.x, 32);
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
xkey = 1;
|
xkey = 1;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case COSE_KEY_LABEL_Y:
|
case COSE_KEY_LABEL_Y:
|
||||||
printf1(TAG_PARSE,"COSE_KEY_LABEL_Y\n");
|
printf1(TAG_PARSE,"COSE_KEY_LABEL_Y\n");
|
||||||
ret = parse_fixed_byte_string(&map, y, 32);
|
ret = parse_fixed_byte_string(&map, cose->pubkey.y, 32);
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
ykey = 1;
|
ykey = 1;
|
||||||
|
|
||||||
@ -1030,7 +1220,7 @@ uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int
|
|||||||
ret = cbor_value_advance(&map);
|
ret = cbor_value_advance(&map);
|
||||||
check_ret(ret);
|
check_ret(ret);
|
||||||
}
|
}
|
||||||
if (xkey == 0 || ykey == 0 || *kty == 0 || *crv == 0)
|
if (xkey == 0 || ykey == 0 || cose->kty == 0 || cose->crv == 0)
|
||||||
{
|
{
|
||||||
return CTAP2_ERR_MISSING_PARAMETER;
|
return CTAP2_ERR_MISSING_PARAMETER;
|
||||||
}
|
}
|
||||||
@ -1110,7 +1300,7 @@ uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length
|
|||||||
break;
|
break;
|
||||||
case CP_keyAgreement:
|
case CP_keyAgreement:
|
||||||
printf1(TAG_CP,"CP_keyAgreement\n");
|
printf1(TAG_CP,"CP_keyAgreement\n");
|
||||||
ret = parse_cose_key(&map, CP->keyAgreement.pubkey.x, CP->keyAgreement.pubkey.y, &CP->keyAgreement.kty, &CP->keyAgreement.crv);
|
ret = parse_cose_key(&map, &CP->keyAgreement);
|
||||||
check_retr(ret);
|
check_retr(ret);
|
||||||
CP->keyAgreementPresent = 1;
|
CP->keyAgreementPresent = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -30,7 +30,7 @@ uint8_t parse_rp(struct rpId * rp, CborValue * val);
|
|||||||
uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up);
|
uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up);
|
||||||
|
|
||||||
uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it);
|
uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it);
|
||||||
uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int * crv);
|
uint8_t parse_cose_key(CborValue * it, COSE_key * cose);
|
||||||
|
|
||||||
|
|
||||||
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length);
|
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length);
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
|
|
||||||
void device_init();
|
void device_init(int argc, char *argv[]);
|
||||||
|
|
||||||
uint32_t millis();
|
uint32_t millis();
|
||||||
|
|
||||||
|
@ -35,6 +35,28 @@ int extension_needs_atomic_count(uint8_t klen, uint8_t * keyh)
|
|||||||
|| ((wallet_request *) keyh)->operation == WalletSign;
|
|| ((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)
|
int16_t bridge_u2f_to_extensions(uint8_t * _chal, uint8_t * _appid, uint8_t klen, uint8_t * keyh)
|
||||||
{
|
{
|
||||||
int8_t ret = 0;
|
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);
|
u2f_response_writeback((uint8_t *)&ret,1);
|
||||||
#ifdef IS_BOOTLOADER
|
#ifdef IS_BOOTLOADER
|
||||||
ret = bootloader_bridge(klen, keyh);
|
ret = bootloader_bridge(klen, keyh);
|
||||||
#elif defined(WALLET_EXTENSION)
|
|
||||||
ret = bridge_u2f_to_wallet(_chal, _appid, klen, keyh);
|
|
||||||
#else
|
#else
|
||||||
ret = bridge_u2f_to_solo(sig, keyh, klen);
|
ret = bridge_u2f_to_solo(sig, keyh, klen);
|
||||||
u2f_response_writeback(sig,72);
|
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)))
|
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));
|
output[0] = bridge_u2f_to_solo(output+1, (uint8_t*)credid, sizeof(CredentialId));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,11 @@
|
|||||||
#include "u2f.h"
|
#include "u2f.h"
|
||||||
#include "apdu.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_u2f(APDU_HEADER * req, uint8_t * payload, uint32_t len);
|
||||||
|
|
||||||
int16_t extend_fido2(CredentialId * credid, uint8_t * output);
|
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);
|
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_ */
|
#endif /* EXTENSIONS_H_ */
|
||||||
|
@ -31,12 +31,15 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include APP_CONFIG
|
#include APP_CONFIG
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// output must be at least 71 bytes
|
// output must be at least 71 bytes
|
||||||
int16_t bridge_u2f_to_solo(uint8_t * output, uint8_t * keyh, int keylen)
|
int16_t bridge_u2f_to_solo(uint8_t * output, uint8_t * keyh, int keylen)
|
||||||
{
|
{
|
||||||
int8_t ret = 0;
|
int8_t ret = 0;
|
||||||
|
|
||||||
wallet_request * req = (wallet_request *) keyh;
|
wallet_request * req = (wallet_request *) keyh;
|
||||||
|
extension_writeback_init(output, 71);
|
||||||
|
|
||||||
printf1(TAG_WALLET, "u2f-solo [%d]: ", keylen); dump_hex1(TAG_WALLET, keyh, keylen);
|
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;
|
break;
|
||||||
|
|
||||||
|
#ifdef ENABLE_WALLET
|
||||||
|
case WalletSign:
|
||||||
|
case WalletRegister:
|
||||||
|
case WalletPin:
|
||||||
|
case WalletReset:
|
||||||
|
return bridge_to_wallet(keyh, keylen);
|
||||||
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
printf2(TAG_ERR,"Invalid wallet command: %x\n",req->operation);
|
printf2(TAG_ERR,"Invalid wallet command: %x\n",req->operation);
|
||||||
ret = CTAP1_ERR_INVALID_COMMAND;
|
ret = CTAP1_ERR_INVALID_COMMAND;
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
|
#include "extensions.h"
|
||||||
|
|
||||||
#if defined(USING_PC) || defined(IS_BOOTLOADER)
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
MBEDTLS_ECP_DP_NONE = 0,
|
MBEDTLS_ECP_DP_NONE = 0,
|
||||||
@ -32,9 +32,7 @@ typedef enum
|
|||||||
MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */
|
MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */
|
||||||
MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */
|
MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */
|
||||||
} mbedtls_ecp_group_id;
|
} mbedtls_ecp_group_id;
|
||||||
#else
|
|
||||||
#include "ecp.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// return 1 if hash is valid, 0 otherwise
|
// 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;
|
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);
|
printf1(TAG_WALLET,"pubkey: "); dump_hex1(TAG_WALLET,KEY_AGREEMENT_PUB,64);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case CP_cmdGetRetries:
|
case CP_cmdGetRetries:
|
||||||
printf1(TAG_WALLET,"cmdGetRetries\n");
|
printf1(TAG_WALLET,"cmdGetRetries\n");
|
||||||
pinTokenEnc[0] = ctap_leftover_pin_attempts();
|
pinTokenEnc[0] = ctap_leftover_pin_attempts();
|
||||||
u2f_response_writeback(pinTokenEnc,1);
|
extension_writeback(pinTokenEnc,1);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case CP_cmdSetPin:
|
case CP_cmdSetPin:
|
||||||
@ -145,7 +143,7 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
printf1(TAG_WALLET,"pinToken: "); dump_hex1(TAG_WALLET, PIN_TOKEN, 16);
|
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;
|
break;
|
||||||
|
|
||||||
@ -159,7 +157,7 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a
|
|||||||
return 0;
|
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];
|
static uint8_t msg_buf[WALLET_MAX_BUFFER];
|
||||||
int reqlen = klen;
|
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_load_external_key(key, keysize);
|
||||||
crypto_ecdsa_sign(args[0], lens[0], sig, MBEDTLS_ECP_DP_SECP256K1);
|
crypto_ecdsa_sign(args[0], lens[0], sig, MBEDTLS_ECP_DP_SECP256K1);
|
||||||
|
|
||||||
u2f_response_writeback(sig,64);
|
extension_writeback(sig,64);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case WalletRegister:
|
case WalletRegister:
|
||||||
@ -374,39 +372,7 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
|
|||||||
|
|
||||||
|
|
||||||
break;
|
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:
|
default:
|
||||||
printf2(TAG_ERR,"Invalid wallet command: %x\n",req->operation);
|
printf2(TAG_ERR,"Invalid wallet command: %x\n",req->operation);
|
||||||
|
@ -87,10 +87,7 @@ typedef enum
|
|||||||
} WalletOperation;
|
} WalletOperation;
|
||||||
|
|
||||||
|
|
||||||
int16_t bridge_u2f_to_extensions(uint8_t * chal, uint8_t * appid, uint8_t klen, uint8_t * keyh);
|
int16_t bridge_to_wallet(uint8_t * keyh, uint8_t klen);
|
||||||
|
|
||||||
// return 1 if request is a wallet request
|
|
||||||
int is_extension_request(uint8_t * req, int len);
|
|
||||||
|
|
||||||
void wallet_init();
|
void wallet_init();
|
||||||
|
|
||||||
|
24
fido2/main.c
24
fido2/main.c
@ -7,6 +7,8 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "cbor.h"
|
#include "cbor.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
@ -19,7 +21,8 @@
|
|||||||
|
|
||||||
#if !defined(TEST)
|
#if !defined(TEST)
|
||||||
|
|
||||||
int main()
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
uint8_t hidmsg[64];
|
uint8_t hidmsg[64];
|
||||||
uint32_t t1 = 0;
|
uint32_t t1 = 0;
|
||||||
@ -27,32 +30,29 @@ int main()
|
|||||||
set_logging_mask(
|
set_logging_mask(
|
||||||
/*0*/
|
/*0*/
|
||||||
//TAG_GEN|
|
//TAG_GEN|
|
||||||
//TAG_MC |
|
// TAG_MC |
|
||||||
//TAG_GA |
|
// TAG_GA |
|
||||||
//TAG_WALLET |
|
TAG_WALLET |
|
||||||
TAG_STOR |
|
TAG_STOR |
|
||||||
//TAG_NFC_APDU |
|
//TAG_NFC_APDU |
|
||||||
TAG_NFC |
|
TAG_NFC |
|
||||||
//TAG_CP |
|
//TAG_CP |
|
||||||
//TAG_CTAP|
|
// TAG_CTAP|
|
||||||
//TAG_HID|
|
//TAG_HID|
|
||||||
//TAG_U2F|
|
TAG_U2F|
|
||||||
//TAG_PARSE |
|
//TAG_PARSE |
|
||||||
//TAG_TIME|
|
//TAG_TIME|
|
||||||
//TAG_DUMP|
|
// TAG_DUMP|
|
||||||
TAG_GREEN|
|
TAG_GREEN|
|
||||||
TAG_RED|
|
TAG_RED|
|
||||||
|
TAG_EXT|
|
||||||
TAG_ERR
|
TAG_ERR
|
||||||
);
|
);
|
||||||
|
|
||||||
device_init();
|
device_init(argc, argv);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
memset(hidmsg,0,sizeof(hidmsg));
|
memset(hidmsg,0,sizeof(hidmsg));
|
||||||
|
|
||||||
// printf1(TAG_GEN,"recv'ing hid msg \n");
|
|
||||||
|
|
||||||
|
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
|
16
fido2/u2f.c
16
fido2/u2f.c
@ -183,21 +183,21 @@ int8_t u2f_new_keypair(struct u2f_key_handle * kh, uint8_t * appid, uint8_t * pu
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Return 1 if authenticate, 0 if not.
|
||||||
static int8_t u2f_appid_eq(struct u2f_key_handle * kh, uint8_t * appid)
|
int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t * appid)
|
||||||
{
|
{
|
||||||
uint8_t tag[U2F_KEY_HANDLE_TAG_SIZE];
|
uint8_t tag[U2F_KEY_HANDLE_TAG_SIZE];
|
||||||
u2f_make_auth_tag(kh, appid, tag);
|
u2f_make_auth_tag(kh, appid, tag);
|
||||||
if (memcmp(kh->tag, tag, U2F_KEY_HANDLE_TAG_SIZE) == 0)
|
if (memcmp(kh->tag, tag, U2F_KEY_HANDLE_TAG_SIZE) == 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf1(TAG_U2F, "key handle + appid not authentic\n");
|
printf1(TAG_U2F, "key handle + appid not authentic\n");
|
||||||
printf1(TAG_U2F, "calc tag: \n"); dump_hex1(TAG_U2F,tag, U2F_KEY_HANDLE_TAG_SIZE);
|
printf1(TAG_U2F, "calc tag: \n"); dump_hex1(TAG_U2F,tag, U2F_KEY_HANDLE_TAG_SIZE);
|
||||||
printf1(TAG_U2F, "inp tag: \n"); dump_hex1(TAG_U2F,kh->tag, U2F_KEY_HANDLE_TAG_SIZE);
|
printf1(TAG_U2F, "inp tag: \n"); dump_hex1(TAG_U2F,kh->tag, U2F_KEY_HANDLE_TAG_SIZE);
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,7 +214,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
|
|||||||
if (control == U2F_AUTHENTICATE_CHECK)
|
if (control == U2F_AUTHENTICATE_CHECK)
|
||||||
{
|
{
|
||||||
printf1(TAG_U2F, "CHECK-ONLY\r\n");
|
printf1(TAG_U2F, "CHECK-ONLY\r\n");
|
||||||
if (u2f_appid_eq(&req->kh, req->app) == 0)
|
if (u2f_authenticate_credential(&req->kh, req->app))
|
||||||
{
|
{
|
||||||
return U2F_SW_CONDITIONS_NOT_SATISFIED;
|
return U2F_SW_CONDITIONS_NOT_SATISFIED;
|
||||||
}
|
}
|
||||||
@ -226,7 +226,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
|
|||||||
if (
|
if (
|
||||||
(control != U2F_AUTHENTICATE_SIGN && control != U2F_AUTHENTICATE_SIGN_NO_USER) ||
|
(control != U2F_AUTHENTICATE_SIGN && control != U2F_AUTHENTICATE_SIGN_NO_USER) ||
|
||||||
req->khl != U2F_KEY_HANDLE_SIZE ||
|
req->khl != U2F_KEY_HANDLE_SIZE ||
|
||||||
u2f_appid_eq(&req->kh, req->app) != 0 || // Order of checks is important
|
(!u2f_authenticate_credential(&req->kh, req->app)) || // Order of checks is important
|
||||||
u2f_load_key(&req->kh, req->app) != 0
|
u2f_load_key(&req->kh, req->app) != 0
|
||||||
|
|
||||||
)
|
)
|
||||||
@ -247,7 +247,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
|
|||||||
}
|
}
|
||||||
|
|
||||||
count = ctap_atomic_count(0);
|
count = ctap_atomic_count(0);
|
||||||
hash[0] = 0x7f;
|
hash[0] = (count >> 24) & 0xff;
|
||||||
hash[1] = (count >> 16) & 0xff;
|
hash[1] = (count >> 16) & 0xff;
|
||||||
hash[2] = (count >> 8) & 0xff;
|
hash[2] = (count >> 8) & 0xff;
|
||||||
hash[3] = (count >> 0) & 0xff;
|
hash[3] = (count >> 0) & 0xff;
|
||||||
@ -264,7 +264,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
|
|||||||
crypto_ecc256_sign(hash, 32, sig);
|
crypto_ecc256_sign(hash, 32, sig);
|
||||||
|
|
||||||
u2f_response_writeback(&up,1);
|
u2f_response_writeback(&up,1);
|
||||||
hash[0] = 0x7f;
|
hash[0] = (count >> 24) & 0xff;
|
||||||
hash[1] = (count >> 16) & 0xff;
|
hash[1] = (count >> 16) & 0xff;
|
||||||
hash[2] = (count >> 8) & 0xff;
|
hash[2] = (count >> 8) & 0xff;
|
||||||
hash[3] = (count >> 0) & 0xff;
|
hash[3] = (count >> 0) & 0xff;
|
||||||
|
@ -103,6 +103,7 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp);
|
|||||||
// @len data length
|
// @len data length
|
||||||
void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp);
|
void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp);
|
||||||
|
|
||||||
|
int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t * appid);
|
||||||
|
|
||||||
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
|
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
|
||||||
void u2f_reset_response();
|
void u2f_reset_response();
|
||||||
|
@ -7,6 +7,7 @@ export PREFIX=/opt/gcc-arm-none-eabi-8-2018-q4-major/bin/
|
|||||||
cd /solo/targets/stm32l432
|
cd /solo/targets/stm32l432
|
||||||
git fetch --tags
|
git fetch --tags
|
||||||
git checkout ${version}
|
git checkout ${version}
|
||||||
|
git submodule update --init --recursive
|
||||||
version=$(git describe)
|
version=$(git describe)
|
||||||
|
|
||||||
make cbor
|
make cbor
|
||||||
@ -34,6 +35,8 @@ function build() {
|
|||||||
build bootloader nonverifying
|
build bootloader nonverifying
|
||||||
build bootloader verifying
|
build bootloader verifying
|
||||||
build firmware hacker solo
|
build firmware hacker solo
|
||||||
|
build firmware hacker-debug-1 solo
|
||||||
|
build firmware hacker-debug-2 solo
|
||||||
build firmware secure solo
|
build firmware secure solo
|
||||||
|
|
||||||
pip install -U pip
|
pip install -U pip
|
||||||
@ -42,3 +45,7 @@ cd ${out_dir}
|
|||||||
bundle="bundle-hacker-${version}"
|
bundle="bundle-hacker-${version}"
|
||||||
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-${version}.hex ${bundle}.hex
|
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-${version}.hex ${bundle}.hex
|
||||||
sha256sum ${bundle}.hex > ${bundle}.sha2
|
sha256sum ${bundle}.hex > ${bundle}.sha2
|
||||||
|
bundle="bundle-hacker-debug-1-${version}"
|
||||||
|
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-1-${version}.hex ${bundle}.hex
|
||||||
|
bundle="bundle-hacker-debug-2-${version}"
|
||||||
|
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-2-${version}.hex ${bundle}.hex
|
||||||
|
5
pc/app.h
5
pc/app.h
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#ifndef SRC_APP_H_
|
#ifndef SRC_APP_H_
|
||||||
#define SRC_APP_H_
|
#define SRC_APP_H_
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define USING_DEV_BOARD
|
#define USING_DEV_BOARD
|
||||||
|
|
||||||
@ -15,11 +16,13 @@
|
|||||||
#define DEBUG_LEVEL 1
|
#define DEBUG_LEVEL 1
|
||||||
|
|
||||||
#define ENABLE_U2F
|
#define ENABLE_U2F
|
||||||
|
#define ENABLE_U2F_EXTENSIONS
|
||||||
//#define BRIDGE_TO_WALLET
|
//#define BRIDGE_TO_WALLET
|
||||||
|
|
||||||
void printing_init();
|
void printing_init();
|
||||||
|
|
||||||
|
extern bool use_udp;
|
||||||
|
|
||||||
// 0xRRGGBB
|
// 0xRRGGBB
|
||||||
#define LED_INIT_VALUE 0x000800
|
#define LED_INIT_VALUE 0x000800
|
||||||
#define LED_WINK_VALUE 0x000008
|
#define LED_WINK_VALUE 0x000008
|
||||||
|
103
pc/device.c
103
pc/device.c
@ -15,6 +15,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "cbor.h"
|
#include "cbor.h"
|
||||||
@ -24,6 +25,8 @@
|
|||||||
|
|
||||||
#define RK_NUM 50
|
#define RK_NUM 50
|
||||||
|
|
||||||
|
bool use_udp = true;
|
||||||
|
|
||||||
struct ResidentKeyStore {
|
struct ResidentKeyStore {
|
||||||
CTAP_residentKey rks[RK_NUM];
|
CTAP_residentKey rks[RK_NUM];
|
||||||
} RK_STORE;
|
} RK_STORE;
|
||||||
@ -118,12 +121,6 @@ void udp_send(int fd, uint8_t * buf, int size)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void udp_close(int fd)
|
|
||||||
{
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t millis()
|
uint32_t millis()
|
||||||
{
|
{
|
||||||
@ -134,18 +131,42 @@ uint32_t millis()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int serverfd = 0;
|
static int fd = 0;
|
||||||
|
|
||||||
void usbhid_init()
|
void usbhid_init()
|
||||||
{
|
{
|
||||||
// just bridge to UDP for now for pure software testing
|
if (use_udp)
|
||||||
serverfd = udp_server();
|
{
|
||||||
|
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
|
// Receive 64 byte USB HID message, don't block, return size of packet, return 0 if nothing
|
||||||
int usbhid_recv(uint8_t * msg)
|
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"
|
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"
|
"\xf3\x33\x48\x5f\x13\xf9\xb2\xda\x34\xc5\xa8\xa3"
|
||||||
"\x40\x52\x66\x97\xa9\xab\x2e\x0b\x39\x4d\x8d\x04"
|
"\x40\x52\x66\x97\xa9\xab\x2e\x0b\x39\x4d\x8d\x04"
|
||||||
@ -166,12 +187,23 @@ int usbhid_recv(uint8_t * msg)
|
|||||||
// Send 64 byte USB HID message
|
// Send 64 byte USB HID message
|
||||||
void usbhid_send(uint8_t * msg)
|
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()
|
void usbhid_close()
|
||||||
{
|
{
|
||||||
udp_close(serverfd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void int_handler(int i)
|
void int_handler(int i)
|
||||||
@ -181,10 +213,47 @@ void int_handler(int i)
|
|||||||
exit(0);
|
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);
|
signal(SIGINT, int_handler);
|
||||||
|
|
||||||
|
printf1(TAG_GREEN, "Using %s backing\n", use_udp ? "UDP" : "hidg");
|
||||||
usbhid_init();
|
usbhid_init();
|
||||||
|
|
||||||
authenticator_initialize();
|
authenticator_initialize();
|
||||||
@ -203,6 +272,14 @@ void main_loop_delay()
|
|||||||
nanosleep(&ts,NULL);
|
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()
|
void heartbeat()
|
||||||
{
|
{
|
||||||
|
@ -93,6 +93,11 @@ flashboot: solo.hex bootloader.hex
|
|||||||
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
|
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
|
||||||
STM32_Programmer_CLI -c port=SWD -halt -d bootloader.hex -rst
|
STM32_Programmer_CLI -c port=SWD -halt -d bootloader.hex -rst
|
||||||
|
|
||||||
|
flash-firmware:
|
||||||
|
arm-none-eabi-size -A solo.elf
|
||||||
|
solo program aux enter-bootloader
|
||||||
|
solo program bootloader solo.hex
|
||||||
|
|
||||||
# tell ST DFU to enter application
|
# tell ST DFU to enter application
|
||||||
detach:
|
detach:
|
||||||
STM32_Programmer_CLI -c port=usb1 -ob nBOOT0=1
|
STM32_Programmer_CLI -c port=usb1 -ob nBOOT0=1
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
CC=arm-none-eabi-gcc
|
|
||||||
CP=arm-none-eabi-objcopy
|
|
||||||
SZ=arm-none-eabi-size
|
|
||||||
AR=arm-none-eabi-ar
|
|
||||||
|
|
||||||
# ST related
|
|
||||||
SRC = src/main.c src/init.c src/flash.c src/led.c
|
|
||||||
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
|
|
||||||
SRC += lib/stm32l4xx_ll_gpio.c lib/stm32l4xx_ll_pwr.c lib/stm32l4xx_ll_rcc.c lib/stm32l4xx_ll_tim.c lib/stm32l4xx_ll_utils.c
|
|
||||||
|
|
||||||
OBJ1=$(SRC:.c=.o)
|
|
||||||
OBJ=$(OBJ1:.s=.o)
|
|
||||||
|
|
||||||
INC = -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ -I../../fido2/ -I../../fido2/extensions
|
|
||||||
INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc
|
|
||||||
INC += -I../../crypto/tiny-AES-c
|
|
||||||
|
|
||||||
SEARCH=-L../../tinycbor/lib
|
|
||||||
|
|
||||||
LDSCRIPT=stm32l432xx.ld
|
|
||||||
|
|
||||||
CFLAGS= $(INC)
|
|
||||||
|
|
||||||
TARGET=solo
|
|
||||||
HW=-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb
|
|
||||||
|
|
||||||
# Solo or Nucleo board
|
|
||||||
CHIP=STM32L432xx
|
|
||||||
|
|
||||||
DEFINES = -D$(CHIP) -DAES256=1 -DUSE_FULL_LL_DRIVER
|
|
||||||
DEFINES += -DTEST_SOLO_STM32 -DTEST
|
|
||||||
|
|
||||||
CFLAGS=$(INC) -c $(DEFINES) -Wall -fdata-sections -ffunction-sections $(HW)
|
|
||||||
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -lnosys
|
|
||||||
LDFLAGS=$(HW) $(LDFLAGS_LIB) -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref
|
|
||||||
|
|
||||||
|
|
||||||
.PRECIOUS: %.o
|
|
||||||
|
|
||||||
all: $(TARGET).elf
|
|
||||||
$(SZ) $^
|
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
|
|
||||||
|
|
||||||
../../crypto/micro-ecc/uECC.o: ../../crypto/micro-ecc/uECC.c
|
|
||||||
$(CC) $^ $(HW) -O3 $(CFLAGS) -o $@
|
|
||||||
|
|
||||||
%.o: %.s
|
|
||||||
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
|
|
||||||
|
|
||||||
%.elf: $(OBJ)
|
|
||||||
$(CC) $^ $(HW) $(LDFLAGS) -o $@
|
|
||||||
|
|
||||||
%.hex: %.elf
|
|
||||||
$(CP) -O ihex $^ $(TARGET).hex
|
|
||||||
$(CP) -O binary $^ $(TARGET).bin
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.o src/*.o src/*.elf *.elf *.hex $(OBJ)
|
|
||||||
|
|
||||||
flash: $(TARGET).hex
|
|
||||||
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
|
|
||||||
STM32_Programmer_CLI -c port=SWD -halt -d $(TARGET).hex -rst
|
|
||||||
|
|
||||||
detach:
|
|
||||||
STM32_Programmer_CLI -c port=usb1 -ob nBOOT0=1
|
|
||||||
|
|
||||||
cbor:
|
|
||||||
cd ../../tinycbor/ && make clean
|
|
||||||
cd ../../tinycbor/ && make CC="$(CC)" AR=$(AR) \
|
|
||||||
LDFLAGS="$(LDFLAGS_LIB)" \
|
|
||||||
CFLAGS="$(CFLAGS)"
|
|
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
uint8_t REBOOT_FLAG = 0;
|
uint8_t REBOOT_FLAG = 0;
|
||||||
|
|
||||||
|
void SystemClock_Config(void);
|
||||||
|
|
||||||
void BOOT_boot(void)
|
void BOOT_boot(void)
|
||||||
{
|
{
|
||||||
|
@ -11,6 +11,7 @@ SRC += ../../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/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
|
||||||
SRC += ../../fido2/ctap_parse.c ../../fido2/main.c
|
SRC += ../../fido2/ctap_parse.c ../../fido2/main.c
|
||||||
SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c
|
SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c
|
||||||
|
SRC += ../../fido2/extensions/wallet.c
|
||||||
|
|
||||||
# Crypto libs
|
# Crypto libs
|
||||||
SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c ../../crypto/tiny-AES-c/aes.c
|
SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c ../../crypto/tiny-AES-c/aes.c
|
||||||
@ -46,7 +47,7 @@ DEFINES = -DDEBUG_LEVEL=$(DEBUG) -D$(CHIP) -DAES256=1 -DUSE_FULL_LL_DRIVER -DAP
|
|||||||
|
|
||||||
CFLAGS=$(INC) -c $(DEFINES) -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fdata-sections -ffunction-sections \
|
CFLAGS=$(INC) -c $(DEFINES) -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fdata-sections -ffunction-sections \
|
||||||
-fomit-frame-pointer $(HW) -g $(VERSION_FLAGS)
|
-fomit-frame-pointer $(HW) -g $(VERSION_FLAGS)
|
||||||
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -u _printf_float -lnosys
|
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -lnosys
|
||||||
LDFLAGS=$(HW) $(LDFLAGS_LIB) -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref -Wl,-Bstatic -ltinycbor
|
LDFLAGS=$(HW) $(LDFLAGS_LIB) -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref -Wl,-Bstatic -ltinycbor
|
||||||
|
|
||||||
ECC_CFLAGS = $(CFLAGS) -DuECC_PLATFORM=5 -DuECC_OPTIMIZATION_LEVEL=4 -DuECC_SQUARE_FUNC=1 -DuECC_SUPPORT_COMPRESSED_POINT=0
|
ECC_CFLAGS = $(CFLAGS) -DuECC_PLATFORM=5 -DuECC_OPTIMIZATION_LEVEL=4 -DuECC_SQUARE_FUNC=1 -DuECC_SUPPORT_COMPRESSED_POINT=0
|
||||||
@ -81,4 +82,4 @@ cbor:
|
|||||||
cd ../../tinycbor/ && make clean
|
cd ../../tinycbor/ && make clean
|
||||||
cd ../../tinycbor/ && make CC="$(CC)" AR=$(AR) \
|
cd ../../tinycbor/ && make CC="$(CC)" AR=$(AR) \
|
||||||
LDFLAGS="$(LDFLAGS_LIB)" \
|
LDFLAGS="$(LDFLAGS_LIB)" \
|
||||||
CFLAGS="$(CFLAGS)"
|
CFLAGS="$(CFLAGS) -Os"
|
||||||
|
@ -110,7 +110,7 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
|
|||||||
0x03, /* bNumEndpoints: 3 endpoints used */
|
0x03, /* bNumEndpoints: 3 endpoints used */
|
||||||
0x02, /* bInterfaceClass: Communication Interface Class */
|
0x02, /* bInterfaceClass: Communication Interface Class */
|
||||||
0x02, /* bInterfaceSubClass: Abstract Control Model */
|
0x02, /* bInterfaceSubClass: Abstract Control Model */
|
||||||
0x01, /* bInterfaceProtocol: Common AT commands */
|
0x00, /* bInterfaceProtocol: Common AT commands */
|
||||||
0x00, /* iInterface: */
|
0x00, /* iInterface: */
|
||||||
|
|
||||||
/*Header Functional Descriptor*/
|
/*Header Functional Descriptor*/
|
||||||
|
@ -1,201 +1,74 @@
|
|||||||
/*
|
/* Copyright 2019 SoloKeys Developers */
|
||||||
*****************************************************************************
|
/* */
|
||||||
**
|
/* Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or */
|
||||||
|
/* http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or */
|
||||||
|
/* http://opensource.org/licenses/MIT>, at your option. This file may not be */
|
||||||
|
/* copied, modified, or distributed except according to those terms. */
|
||||||
|
|
||||||
** File : LinkerScript.ld
|
|
||||||
**
|
|
||||||
** Abstract : Linker script for STM32L432KCUx Device with
|
|
||||||
** 256KByte FLASH, 64KByte RAM
|
|
||||||
**
|
|
||||||
** Set heap size, stack size and stack location according
|
|
||||||
** to application requirements.
|
|
||||||
**
|
|
||||||
** Set memory bank area and size if external memory is used.
|
|
||||||
**
|
|
||||||
** Target : STMicroelectronics STM32
|
|
||||||
**
|
|
||||||
**
|
|
||||||
** Distribution: The file is distributed as is, without any warranty
|
|
||||||
** of any kind.
|
|
||||||
**
|
|
||||||
** (c)Copyright Ac6.
|
|
||||||
** You may use this file as-is or modify it according to the needs of your
|
|
||||||
** project. Distribution of this file (unmodified or modified) is not
|
|
||||||
** permitted. Ac6 permit registered System Workbench for MCU users the
|
|
||||||
** rights to distribute the assembled, compiled & linked contents of this
|
|
||||||
** file as part of an application binary file, provided that it is built
|
|
||||||
** using the System Workbench for MCU toolchain.
|
|
||||||
**
|
|
||||||
*****************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Entry Point */
|
|
||||||
ENTRY(Reset_Handler)
|
ENTRY(Reset_Handler)
|
||||||
|
|
||||||
/* Highest address of the user mode stack */
|
/* End of RAM */
|
||||||
_estack = 0x2000c000; /* end of RAM */
|
_estack = 0x2000c000;
|
||||||
/* Generate a link error if heap and stack don't fit into RAM */
|
|
||||||
_Min_Heap_Size = 0x200; /* required amount of heap */
|
_MIN_STACK_SIZE = 0x400;
|
||||||
_Min_Stack_Size = 0x400; /* required amount of stack */
|
|
||||||
|
|
||||||
/* Specify the memory areas */
|
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 20K
|
flash (rx) : ORIGIN = 0x08000000, LENGTH = 20K
|
||||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||||
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Define output sections */
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
/* The startup code goes first into FLASH */
|
.isr_vector :
|
||||||
.isr_vector :
|
{
|
||||||
{
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
KEEP(*(.isr_vector))
|
||||||
KEEP(*(.isr_vector)) /* Startup code */
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
} >flash
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
/* The program code and other data goes into FLASH */
|
.text :
|
||||||
.text :
|
{
|
||||||
{
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
*(.text*)
|
||||||
*(.text) /* .text sections (code) */
|
*(.rodata*)
|
||||||
*(.text*) /* .text* sections (code) */
|
KEEP(*(.init))
|
||||||
*(.glue_7) /* glue arm to thumb code */
|
KEEP(*(.finit))
|
||||||
*(.glue_7t) /* glue thumb to arm code */
|
. = ALIGN(8);
|
||||||
*(.eh_frame)
|
_etext = .;
|
||||||
|
} >flash
|
||||||
|
|
||||||
KEEP (*(.init))
|
_sidata = LOADADDR(.data);
|
||||||
KEEP (*(.fini))
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
.data :
|
||||||
_etext = .; /* define a global symbols at end of code */
|
{
|
||||||
} >FLASH
|
. = ALIGN(8);
|
||||||
|
_sdata = .;
|
||||||
|
*(.data*)
|
||||||
|
. = ALIGN(8);
|
||||||
|
_edata = .;
|
||||||
|
} >ram AT> flash
|
||||||
|
|
||||||
/* Constant data goes into FLASH */
|
.bss :
|
||||||
.rodata :
|
{
|
||||||
{
|
. = ALIGN(4);
|
||||||
. = ALIGN(8);
|
_sbss = .;
|
||||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
__bss_start__ = _sbss;
|
||||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
*(.bss*)
|
||||||
. = ALIGN(8);
|
*(COMMON)
|
||||||
} >FLASH
|
. = ALIGN(4);
|
||||||
|
_ebss = .;
|
||||||
|
__bss_end__ = _ebss;
|
||||||
|
} > ram
|
||||||
|
|
||||||
.ARM.extab :
|
._stack :
|
||||||
{
|
{
|
||||||
. = ALIGN(8);
|
. = ALIGN(8);
|
||||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
end = .;
|
||||||
. = ALIGN(8);
|
_end = .;
|
||||||
} >FLASH
|
. = . + _MIN_STACK_SIZE;
|
||||||
.ARM : {
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
} > ram
|
||||||
__exidx_start = .;
|
|
||||||
*(.ARM.exidx*)
|
|
||||||
__exidx_end = .;
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
.preinit_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
|
||||||
KEEP (*(.preinit_array*))
|
|
||||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
.init_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__init_array_start = .);
|
|
||||||
KEEP (*(SORT(.init_array.*)))
|
|
||||||
KEEP (*(.init_array*))
|
|
||||||
PROVIDE_HIDDEN (__init_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
.fini_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
|
||||||
KEEP (*(SORT(.fini_array.*)))
|
|
||||||
KEEP (*(.fini_array*))
|
|
||||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
/* used by the startup to initialize data */
|
|
||||||
_sidata = LOADADDR(.data);
|
|
||||||
|
|
||||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
|
||||||
.data :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
_sdata = .; /* create a global symbol at data start */
|
|
||||||
*(.data) /* .data sections */
|
|
||||||
*(.data*) /* .data* sections */
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
|
||||||
_edata = .; /* define a global symbol at data end */
|
|
||||||
} >RAM AT> FLASH
|
|
||||||
|
|
||||||
_sisram2 = LOADADDR(.sram2);
|
|
||||||
|
|
||||||
/* CCM-RAM section
|
|
||||||
*
|
|
||||||
* IMPORTANT NOTE!
|
|
||||||
* If initialized variables will be placed in this section,
|
|
||||||
* the startup code needs to be modified to copy the init-values.
|
|
||||||
*/
|
|
||||||
.sram2 :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
_ssram2 = .; /* create a global symbol at sram2 start */
|
|
||||||
*(.sram2)
|
|
||||||
*(.sram2*)
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
|
||||||
_esram2 = .; /* create a global symbol at sram2 end */
|
|
||||||
} >SRAM2 AT> FLASH
|
|
||||||
|
|
||||||
|
|
||||||
/* Uninitialized data section */
|
|
||||||
. = ALIGN(4);
|
|
||||||
.bss :
|
|
||||||
{
|
|
||||||
/* This is used by the startup in order to initialize the .bss secion */
|
|
||||||
_sbss = .; /* define a global symbol at bss start */
|
|
||||||
__bss_start__ = _sbss;
|
|
||||||
*(.bss)
|
|
||||||
*(.bss*)
|
|
||||||
*(COMMON)
|
|
||||||
|
|
||||||
. = ALIGN(4);
|
|
||||||
_ebss = .; /* define a global symbol at bss end */
|
|
||||||
__bss_end__ = _ebss;
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
|
||||||
._user_heap_stack :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE ( end = . );
|
|
||||||
PROVIDE ( _end = . );
|
|
||||||
. = . + _Min_Heap_Size;
|
|
||||||
. = . + _Min_Stack_Size;
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Remove information from the standard libraries */
|
|
||||||
/DISCARD/ :
|
|
||||||
{
|
|
||||||
libc.a ( * )
|
|
||||||
libm.a ( * )
|
|
||||||
libgcc.a ( * )
|
|
||||||
}
|
|
||||||
|
|
||||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
|
||||||
}
|
}
|
||||||
|
@ -1,201 +1,74 @@
|
|||||||
/*
|
/* Copyright 2019 SoloKeys Developers */
|
||||||
*****************************************************************************
|
/* */
|
||||||
**
|
/* Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or */
|
||||||
|
/* http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or */
|
||||||
|
/* http://opensource.org/licenses/MIT>, at your option. This file may not be */
|
||||||
|
/* copied, modified, or distributed except according to those terms. */
|
||||||
|
|
||||||
** File : LinkerScript.ld
|
|
||||||
**
|
|
||||||
** Abstract : Linker script for STM32L432KCUx Device with
|
|
||||||
** 256KByte FLASH, 64KByte RAM
|
|
||||||
**
|
|
||||||
** Set heap size, stack size and stack location according
|
|
||||||
** to application requirements.
|
|
||||||
**
|
|
||||||
** Set memory bank area and size if external memory is used.
|
|
||||||
**
|
|
||||||
** Target : STMicroelectronics STM32
|
|
||||||
**
|
|
||||||
**
|
|
||||||
** Distribution: The file is distributed as is, without any warranty
|
|
||||||
** of any kind.
|
|
||||||
**
|
|
||||||
** (c)Copyright Ac6.
|
|
||||||
** You may use this file as-is or modify it according to the needs of your
|
|
||||||
** project. Distribution of this file (unmodified or modified) is not
|
|
||||||
** permitted. Ac6 permit registered System Workbench for MCU users the
|
|
||||||
** rights to distribute the assembled, compiled & linked contents of this
|
|
||||||
** file as part of an application binary file, provided that it is built
|
|
||||||
** using the System Workbench for MCU toolchain.
|
|
||||||
**
|
|
||||||
*****************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Entry Point */
|
|
||||||
ENTRY(Reset_Handler)
|
ENTRY(Reset_Handler)
|
||||||
|
|
||||||
/* Highest address of the user mode stack */
|
/* End of RAM */
|
||||||
_estack = 0x2000c000; /* end of RAM */
|
_estack = 0x2000c000;
|
||||||
/* Generate a link error if heap and stack don't fit into RAM */
|
|
||||||
_Min_Heap_Size = 0x200; /* required amount of heap */
|
_MIN_STACK_SIZE = 0x400;
|
||||||
_Min_Stack_Size = 0x400; /* required amount of stack */
|
|
||||||
|
|
||||||
/* Specify the memory areas */
|
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
|
flash (rx) : ORIGIN = 0x08000000, LENGTH = 32K
|
||||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||||
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Define output sections */
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
/* The startup code goes first into FLASH */
|
.isr_vector :
|
||||||
.isr_vector :
|
{
|
||||||
{
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
KEEP(*(.isr_vector))
|
||||||
KEEP(*(.isr_vector)) /* Startup code */
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
} >flash
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
/* The program code and other data goes into FLASH */
|
.text :
|
||||||
.text :
|
{
|
||||||
{
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
*(.text*)
|
||||||
*(.text) /* .text sections (code) */
|
*(.rodata*)
|
||||||
*(.text*) /* .text* sections (code) */
|
KEEP(*(.init))
|
||||||
*(.glue_7) /* glue arm to thumb code */
|
KEEP(*(.finit))
|
||||||
*(.glue_7t) /* glue thumb to arm code */
|
. = ALIGN(8);
|
||||||
*(.eh_frame)
|
_etext = .;
|
||||||
|
} >flash
|
||||||
|
|
||||||
KEEP (*(.init))
|
_sidata = LOADADDR(.data);
|
||||||
KEEP (*(.fini))
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
.data :
|
||||||
_etext = .; /* define a global symbols at end of code */
|
{
|
||||||
} >FLASH
|
. = ALIGN(8);
|
||||||
|
_sdata = .;
|
||||||
|
*(.data*)
|
||||||
|
. = ALIGN(8);
|
||||||
|
_edata = .;
|
||||||
|
} >ram AT> flash
|
||||||
|
|
||||||
/* Constant data goes into FLASH */
|
.bss :
|
||||||
.rodata :
|
{
|
||||||
{
|
. = ALIGN(4);
|
||||||
. = ALIGN(8);
|
_sbss = .;
|
||||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
__bss_start__ = _sbss;
|
||||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
*(.bss*)
|
||||||
. = ALIGN(8);
|
*(COMMON)
|
||||||
} >FLASH
|
. = ALIGN(4);
|
||||||
|
_ebss = .;
|
||||||
|
__bss_end__ = _ebss;
|
||||||
|
} > ram
|
||||||
|
|
||||||
.ARM.extab :
|
._stack :
|
||||||
{
|
{
|
||||||
. = ALIGN(8);
|
. = ALIGN(8);
|
||||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
end = .;
|
||||||
. = ALIGN(8);
|
_end = .;
|
||||||
} >FLASH
|
. = . + _MIN_STACK_SIZE;
|
||||||
.ARM : {
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
} > ram
|
||||||
__exidx_start = .;
|
|
||||||
*(.ARM.exidx*)
|
|
||||||
__exidx_end = .;
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
.preinit_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
|
||||||
KEEP (*(.preinit_array*))
|
|
||||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
.init_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__init_array_start = .);
|
|
||||||
KEEP (*(SORT(.init_array.*)))
|
|
||||||
KEEP (*(.init_array*))
|
|
||||||
PROVIDE_HIDDEN (__init_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
.fini_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
|
||||||
KEEP (*(SORT(.fini_array.*)))
|
|
||||||
KEEP (*(.fini_array*))
|
|
||||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
/* used by the startup to initialize data */
|
|
||||||
_sidata = LOADADDR(.data);
|
|
||||||
|
|
||||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
|
||||||
.data :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
_sdata = .; /* create a global symbol at data start */
|
|
||||||
*(.data) /* .data sections */
|
|
||||||
*(.data*) /* .data* sections */
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
|
||||||
_edata = .; /* define a global symbol at data end */
|
|
||||||
} >RAM AT> FLASH
|
|
||||||
|
|
||||||
_sisram2 = LOADADDR(.sram2);
|
|
||||||
|
|
||||||
/* CCM-RAM section
|
|
||||||
*
|
|
||||||
* IMPORTANT NOTE!
|
|
||||||
* If initialized variables will be placed in this section,
|
|
||||||
* the startup code needs to be modified to copy the init-values.
|
|
||||||
*/
|
|
||||||
.sram2 :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
_ssram2 = .; /* create a global symbol at sram2 start */
|
|
||||||
*(.sram2)
|
|
||||||
*(.sram2*)
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
|
||||||
_esram2 = .; /* create a global symbol at sram2 end */
|
|
||||||
} >SRAM2 AT> FLASH
|
|
||||||
|
|
||||||
|
|
||||||
/* Uninitialized data section */
|
|
||||||
. = ALIGN(4);
|
|
||||||
.bss :
|
|
||||||
{
|
|
||||||
/* This is used by the startup in order to initialize the .bss secion */
|
|
||||||
_sbss = .; /* define a global symbol at bss start */
|
|
||||||
__bss_start__ = _sbss;
|
|
||||||
*(.bss)
|
|
||||||
*(.bss*)
|
|
||||||
*(COMMON)
|
|
||||||
|
|
||||||
. = ALIGN(4);
|
|
||||||
_ebss = .; /* define a global symbol at bss end */
|
|
||||||
__bss_end__ = _ebss;
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
|
||||||
._user_heap_stack :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE ( end = . );
|
|
||||||
PROVIDE ( _end = . );
|
|
||||||
. = . + _Min_Heap_Size;
|
|
||||||
. = . + _Min_Stack_Size;
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Remove information from the standard libraries */
|
|
||||||
/DISCARD/ :
|
|
||||||
{
|
|
||||||
libc.a ( * )
|
|
||||||
libm.a ( * )
|
|
||||||
libgcc.a ( * )
|
|
||||||
}
|
|
||||||
|
|
||||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
|
||||||
}
|
}
|
||||||
|
@ -1,202 +1,80 @@
|
|||||||
/*
|
/* Copyright 2019 SoloKeys Developers */
|
||||||
*****************************************************************************
|
/* */
|
||||||
**
|
/* Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or */
|
||||||
|
/* http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or */
|
||||||
|
/* http://opensource.org/licenses/MIT>, at your option. This file may not be */
|
||||||
|
/* copied, modified, or distributed except according to those terms. */
|
||||||
|
|
||||||
** File : LinkerScript.ld
|
|
||||||
**
|
|
||||||
** Abstract : Linker script for STM32L432KCUx Device with
|
|
||||||
** 256KByte FLASH, 64KByte RAM
|
|
||||||
**
|
|
||||||
** Set heap size, stack size and stack location according
|
|
||||||
** to application requirements.
|
|
||||||
**
|
|
||||||
** Set memory bank area and size if external memory is used.
|
|
||||||
**
|
|
||||||
** Target : STMicroelectronics STM32
|
|
||||||
**
|
|
||||||
**
|
|
||||||
** Distribution: The file is distributed as is, without any warranty
|
|
||||||
** of any kind.
|
|
||||||
**
|
|
||||||
** (c)Copyright Ac6.
|
|
||||||
** You may use this file as-is or modify it according to the needs of your
|
|
||||||
** project. Distribution of this file (unmodified or modified) is not
|
|
||||||
** permitted. Ac6 permit registered System Workbench for MCU users the
|
|
||||||
** rights to distribute the assembled, compiled & linked contents of this
|
|
||||||
** file as part of an application binary file, provided that it is built
|
|
||||||
** using the System Workbench for MCU toolchain.
|
|
||||||
**
|
|
||||||
*****************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Entry Point */
|
|
||||||
ENTRY(Reset_Handler)
|
ENTRY(Reset_Handler)
|
||||||
|
|
||||||
/* Highest address of the user mode stack */
|
/* End of RAM */
|
||||||
_estack = 0x2000c000; /* end of RAM */
|
_estack = 0x2000c000;
|
||||||
/* Generate a link error if heap and stack don't fit into RAM */
|
|
||||||
_Min_Heap_Size = 0x200; /* required amount of heap */
|
_MIN_STACK_SIZE = 0x400;
|
||||||
_Min_Stack_Size = 0x400; /* required amount of stack */
|
|
||||||
|
/*
|
||||||
|
Memory layout of device:
|
||||||
|
20 KB 198KB-8 38 KB
|
||||||
|
| bootloader | application | secrets/data |
|
||||||
|
*/
|
||||||
|
|
||||||
/* Specify the memory areas */
|
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
/* First 20 KB is bootloader */
|
flash (rx) : ORIGIN = 0x08005000, LENGTH = 198K - 8
|
||||||
FLASH (rx) : ORIGIN = 0x08005000, LENGTH = 198K-8 /* Leave out 38 Kb at end for data */
|
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||||
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Define output sections */
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
/* The startup code goes first into FLASH */
|
.isr_vector :
|
||||||
.isr_vector :
|
{
|
||||||
{
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
KEEP(*(.isr_vector))
|
||||||
KEEP(*(.isr_vector)) /* Startup code */
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
} >flash
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
/* The program code and other data goes into FLASH */
|
.text :
|
||||||
.text :
|
{
|
||||||
{
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
*(.text*)
|
||||||
*(.text) /* .text sections (code) */
|
*(.rodata*)
|
||||||
*(.text*) /* .text* sections (code) */
|
KEEP(*(.init))
|
||||||
*(.glue_7) /* glue arm to thumb code */
|
KEEP(*(.finit))
|
||||||
*(.glue_7t) /* glue thumb to arm code */
|
. = ALIGN(8);
|
||||||
*(.eh_frame)
|
_etext = .;
|
||||||
|
} >flash
|
||||||
|
|
||||||
KEEP (*(.init))
|
_sidata = LOADADDR(.data);
|
||||||
KEEP (*(.fini))
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
.data :
|
||||||
_etext = .; /* define a global symbols at end of code */
|
{
|
||||||
} >FLASH
|
. = ALIGN(8);
|
||||||
|
_sdata = .;
|
||||||
|
*(.data*)
|
||||||
|
. = ALIGN(8);
|
||||||
|
_edata = .;
|
||||||
|
} >ram AT> flash
|
||||||
|
|
||||||
/* Constant data goes into FLASH */
|
.bss :
|
||||||
.rodata :
|
{
|
||||||
{
|
. = ALIGN(4);
|
||||||
. = ALIGN(8);
|
_sbss = .;
|
||||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
__bss_start__ = _sbss;
|
||||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
*(.bss*)
|
||||||
. = ALIGN(8);
|
*(COMMON)
|
||||||
} >FLASH
|
. = ALIGN(4);
|
||||||
|
_ebss = .;
|
||||||
|
__bss_end__ = _ebss;
|
||||||
|
} > ram
|
||||||
|
|
||||||
.ARM.extab :
|
._stack :
|
||||||
{
|
{
|
||||||
. = ALIGN(8);
|
. = ALIGN(8);
|
||||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
end = .;
|
||||||
. = ALIGN(8);
|
_end = .;
|
||||||
} >FLASH
|
. = . + _MIN_STACK_SIZE;
|
||||||
.ARM : {
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
} > ram
|
||||||
__exidx_start = .;
|
|
||||||
*(.ARM.exidx*)
|
|
||||||
__exidx_end = .;
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
.preinit_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
|
||||||
KEEP (*(.preinit_array*))
|
|
||||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
.init_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__init_array_start = .);
|
|
||||||
KEEP (*(SORT(.init_array.*)))
|
|
||||||
KEEP (*(.init_array*))
|
|
||||||
PROVIDE_HIDDEN (__init_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
.fini_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
|
||||||
KEEP (*(SORT(.fini_array.*)))
|
|
||||||
KEEP (*(.fini_array*))
|
|
||||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
/* used by the startup to initialize data */
|
|
||||||
_sidata = LOADADDR(.data);
|
|
||||||
|
|
||||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
|
||||||
.data :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
_sdata = .; /* create a global symbol at data start */
|
|
||||||
*(.data) /* .data sections */
|
|
||||||
*(.data*) /* .data* sections */
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
|
||||||
_edata = .; /* define a global symbol at data end */
|
|
||||||
} >RAM AT> FLASH
|
|
||||||
|
|
||||||
_sisram2 = LOADADDR(.sram2);
|
|
||||||
|
|
||||||
/* CCM-RAM section
|
|
||||||
*
|
|
||||||
* IMPORTANT NOTE!
|
|
||||||
* If initialized variables will be placed in this section,
|
|
||||||
* the startup code needs to be modified to copy the init-values.
|
|
||||||
*/
|
|
||||||
.sram2 :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
_ssram2 = .; /* create a global symbol at sram2 start */
|
|
||||||
*(.sram2)
|
|
||||||
*(.sram2*)
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
|
||||||
_esram2 = .; /* create a global symbol at sram2 end */
|
|
||||||
} >SRAM2 AT> FLASH
|
|
||||||
|
|
||||||
|
|
||||||
/* Uninitialized data section */
|
|
||||||
. = ALIGN(4);
|
|
||||||
.bss :
|
|
||||||
{
|
|
||||||
/* This is used by the startup in order to initialize the .bss secion */
|
|
||||||
_sbss = .; /* define a global symbol at bss start */
|
|
||||||
__bss_start__ = _sbss;
|
|
||||||
*(.bss)
|
|
||||||
*(.bss*)
|
|
||||||
*(COMMON)
|
|
||||||
|
|
||||||
. = ALIGN(4);
|
|
||||||
_ebss = .; /* define a global symbol at bss end */
|
|
||||||
__bss_end__ = _ebss;
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
|
||||||
._user_heap_stack :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE ( end = . );
|
|
||||||
PROVIDE ( _end = . );
|
|
||||||
. = . + _Min_Heap_Size;
|
|
||||||
. = . + _Min_Stack_Size;
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Remove information from the standard libraries */
|
|
||||||
/DISCARD/ :
|
|
||||||
{
|
|
||||||
libc.a ( * )
|
|
||||||
libm.a ( * )
|
|
||||||
libgcc.a ( * )
|
|
||||||
}
|
|
||||||
|
|
||||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
|
||||||
}
|
}
|
||||||
|
@ -1,203 +1,74 @@
|
|||||||
/*
|
/* Copyright 2019 SoloKeys Developers */
|
||||||
*****************************************************************************
|
/* */
|
||||||
**
|
/* Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or */
|
||||||
|
/* http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or */
|
||||||
|
/* http://opensource.org/licenses/MIT>, at your option. This file may not be */
|
||||||
|
/* copied, modified, or distributed except according to those terms. */
|
||||||
|
|
||||||
** File : LinkerScript.ld
|
|
||||||
**
|
|
||||||
** Abstract : Linker script for STM32L432KCUx Device with
|
|
||||||
** 256KByte FLASH, 64KByte RAM
|
|
||||||
**
|
|
||||||
** Set heap size, stack size and stack location according
|
|
||||||
** to application requirements.
|
|
||||||
**
|
|
||||||
** Set memory bank area and size if external memory is used.
|
|
||||||
**
|
|
||||||
** Target : STMicroelectronics STM32
|
|
||||||
**
|
|
||||||
**
|
|
||||||
** Distribution: The file is distributed as is, without any warranty
|
|
||||||
** of any kind.
|
|
||||||
**
|
|
||||||
** (c)Copyright Ac6.
|
|
||||||
** You may use this file as-is or modify it according to the needs of your
|
|
||||||
** project. Distribution of this file (unmodified or modified) is not
|
|
||||||
** permitted. Ac6 permit registered System Workbench for MCU users the
|
|
||||||
** rights to distribute the assembled, compiled & linked contents of this
|
|
||||||
** file as part of an application binary file, provided that it is built
|
|
||||||
** using the System Workbench for MCU toolchain.
|
|
||||||
**
|
|
||||||
*****************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Entry Point */
|
|
||||||
ENTRY(Reset_Handler)
|
ENTRY(Reset_Handler)
|
||||||
|
|
||||||
/* Highest address of the user mode stack */
|
/* End of RAM */
|
||||||
_estack = 0x2000c000; /* end of RAM */
|
_estack = 0x2000c000;
|
||||||
/* Generate a link error if heap and stack don't fit into RAM */
|
|
||||||
_Min_Heap_Size = 0x200; /* required amount of heap */
|
_MIN_STACK_SIZE = 0x400;
|
||||||
_Min_Stack_Size = 0x400; /* required amount of stack */
|
|
||||||
|
|
||||||
/* Specify the memory areas */
|
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
/* First 32 KB is bootloader */
|
flash (rx) : ORIGIN = 0x08008000, LENGTH = 186K - 8
|
||||||
/*FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 238K-8 [> Leave out 38 Kb at end for data <]*/
|
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||||
FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 186K-8 /* Leave out 38 Kb at end for data */
|
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
|
||||||
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Define output sections */
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
/* The startup code goes first into FLASH */
|
.isr_vector :
|
||||||
.isr_vector :
|
{
|
||||||
{
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
KEEP(*(.isr_vector))
|
||||||
KEEP(*(.isr_vector)) /* Startup code */
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
} >flash
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
/* The program code and other data goes into FLASH */
|
.text :
|
||||||
.text :
|
{
|
||||||
{
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
*(.text*)
|
||||||
*(.text) /* .text sections (code) */
|
*(.rodata*)
|
||||||
*(.text*) /* .text* sections (code) */
|
KEEP(*(.init))
|
||||||
*(.glue_7) /* glue arm to thumb code */
|
KEEP(*(.finit))
|
||||||
*(.glue_7t) /* glue thumb to arm code */
|
. = ALIGN(8);
|
||||||
*(.eh_frame)
|
_etext = .;
|
||||||
|
} >flash
|
||||||
|
|
||||||
KEEP (*(.init))
|
_sidata = LOADADDR(.data);
|
||||||
KEEP (*(.fini))
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
.data :
|
||||||
_etext = .; /* define a global symbols at end of code */
|
{
|
||||||
} >FLASH
|
. = ALIGN(8);
|
||||||
|
_sdata = .;
|
||||||
|
*(.data*)
|
||||||
|
. = ALIGN(8);
|
||||||
|
_edata = .;
|
||||||
|
} >ram AT> flash
|
||||||
|
|
||||||
/* Constant data goes into FLASH */
|
.bss :
|
||||||
.rodata :
|
{
|
||||||
{
|
. = ALIGN(4);
|
||||||
. = ALIGN(8);
|
_sbss = .;
|
||||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
__bss_start__ = _sbss;
|
||||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
*(.bss*)
|
||||||
. = ALIGN(8);
|
*(COMMON)
|
||||||
} >FLASH
|
. = ALIGN(4);
|
||||||
|
_ebss = .;
|
||||||
|
__bss_end__ = _ebss;
|
||||||
|
} > ram
|
||||||
|
|
||||||
.ARM.extab :
|
._stack :
|
||||||
{
|
{
|
||||||
. = ALIGN(8);
|
. = ALIGN(8);
|
||||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
end = .;
|
||||||
. = ALIGN(8);
|
_end = .;
|
||||||
} >FLASH
|
. = . + _MIN_STACK_SIZE;
|
||||||
.ARM : {
|
. = ALIGN(8);
|
||||||
. = ALIGN(8);
|
} > ram
|
||||||
__exidx_start = .;
|
|
||||||
*(.ARM.exidx*)
|
|
||||||
__exidx_end = .;
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
.preinit_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
|
||||||
KEEP (*(.preinit_array*))
|
|
||||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
.init_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__init_array_start = .);
|
|
||||||
KEEP (*(SORT(.init_array.*)))
|
|
||||||
KEEP (*(.init_array*))
|
|
||||||
PROVIDE_HIDDEN (__init_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
.fini_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
|
||||||
KEEP (*(SORT(.fini_array.*)))
|
|
||||||
KEEP (*(.fini_array*))
|
|
||||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
/* used by the startup to initialize data */
|
|
||||||
_sidata = LOADADDR(.data);
|
|
||||||
|
|
||||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
|
||||||
.data :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
_sdata = .; /* create a global symbol at data start */
|
|
||||||
*(.data) /* .data sections */
|
|
||||||
*(.data*) /* .data* sections */
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
|
||||||
_edata = .; /* define a global symbol at data end */
|
|
||||||
} >RAM AT> FLASH
|
|
||||||
|
|
||||||
_sisram2 = LOADADDR(.sram2);
|
|
||||||
|
|
||||||
/* CCM-RAM section
|
|
||||||
*
|
|
||||||
* IMPORTANT NOTE!
|
|
||||||
* If initialized variables will be placed in this section,
|
|
||||||
* the startup code needs to be modified to copy the init-values.
|
|
||||||
*/
|
|
||||||
.sram2 :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
_ssram2 = .; /* create a global symbol at sram2 start */
|
|
||||||
*(.sram2)
|
|
||||||
*(.sram2*)
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
|
||||||
_esram2 = .; /* create a global symbol at sram2 end */
|
|
||||||
} >SRAM2 AT> FLASH
|
|
||||||
|
|
||||||
|
|
||||||
/* Uninitialized data section */
|
|
||||||
. = ALIGN(4);
|
|
||||||
.bss :
|
|
||||||
{
|
|
||||||
/* This is used by the startup in order to initialize the .bss secion */
|
|
||||||
_sbss = .; /* define a global symbol at bss start */
|
|
||||||
__bss_start__ = _sbss;
|
|
||||||
*(.bss)
|
|
||||||
*(.bss*)
|
|
||||||
*(COMMON)
|
|
||||||
|
|
||||||
. = ALIGN(4);
|
|
||||||
_ebss = .; /* define a global symbol at bss end */
|
|
||||||
__bss_end__ = _ebss;
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
|
||||||
._user_heap_stack :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE ( end = . );
|
|
||||||
PROVIDE ( _end = . );
|
|
||||||
. = . + _Min_Heap_Size;
|
|
||||||
. = . + _Min_Stack_Size;
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Remove information from the standard libraries */
|
|
||||||
/DISCARD/ :
|
|
||||||
{
|
|
||||||
libc.a ( * )
|
|
||||||
libm.a ( * )
|
|
||||||
libgcc.a ( * )
|
|
||||||
}
|
|
||||||
|
|
||||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
//#define USING_DEV_BOARD
|
//#define USING_DEV_BOARD
|
||||||
|
|
||||||
#define ENABLE_U2F_EXTENSIONS
|
#define ENABLE_U2F_EXTENSIONS
|
||||||
|
// #define ENABLE_WALLET
|
||||||
|
|
||||||
#define ENABLE_U2F
|
#define ENABLE_U2F
|
||||||
|
|
||||||
|
@ -107,17 +107,17 @@ void device_reboot()
|
|||||||
NVIC_SystemReset();
|
NVIC_SystemReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void device_init()
|
void device_init(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
|
||||||
hw_init(LOW_FREQUENCY);
|
hw_init(LOW_FREQUENCY);
|
||||||
isLowFreq = 0;
|
|
||||||
|
|
||||||
haveNFC = nfc_init();
|
haveNFC = nfc_init();
|
||||||
|
|
||||||
if (haveNFC)
|
if (haveNFC)
|
||||||
{
|
{
|
||||||
printf1(TAG_NFC, "Have NFC\r\n");
|
printf1(TAG_NFC, "Have NFC\r\n");
|
||||||
|
isLowFreq = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -460,6 +460,10 @@ static int handle_packets()
|
|||||||
int ctap_user_presence_test()
|
int ctap_user_presence_test()
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
if (device_is_nfc())
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
#if SKIP_BUTTON_CHECK_WITH_DELAY
|
#if SKIP_BUTTON_CHECK_WITH_DELAY
|
||||||
int i=500;
|
int i=500;
|
||||||
while(i--)
|
while(i--)
|
||||||
|
@ -496,9 +496,9 @@ void nfc_process_iblock(uint8_t * buf, int len)
|
|||||||
// WTX_on(WTX_TIME_DEFAULT);
|
// WTX_on(WTX_TIME_DEFAULT);
|
||||||
// SystemClock_Config_LF32();
|
// SystemClock_Config_LF32();
|
||||||
// delay(300);
|
// delay(300);
|
||||||
device_set_clock_rate(DEVICE_LOW_POWER_FAST);;
|
if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_FAST);;
|
||||||
u2f_request_nfc(&buf[1], len, &ctap_resp);
|
u2f_request_nfc(&buf[1], len, &ctap_resp);
|
||||||
device_set_clock_rate(DEVICE_LOW_POWER_IDLE);;
|
if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);;
|
||||||
// if (!WTX_off())
|
// if (!WTX_off())
|
||||||
// return;
|
// return;
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ void _putchar(char c)
|
|||||||
int _write (int fd, const void *buf, unsigned long int len)
|
int _write (int fd, const void *buf, unsigned long int len)
|
||||||
{
|
{
|
||||||
uint8_t * data = (uint8_t *) buf;
|
uint8_t * data = (uint8_t *) buf;
|
||||||
#if DEBUG_LEVEL>1
|
#if DEBUG_LEVEL>0
|
||||||
// static uint8_t logbuf[1000] = {0};
|
// static uint8_t logbuf[1000] = {0};
|
||||||
// static int logbuflen = 0;
|
// static int logbuflen = 0;
|
||||||
// if (logbuflen + len > sizeof(logbuf)) {
|
// if (logbuflen + len > sizeof(logbuf)) {
|
||||||
@ -39,7 +39,7 @@ int _write (int fd, const void *buf, unsigned long int len)
|
|||||||
// logbuflen += len;
|
// logbuflen += len;
|
||||||
|
|
||||||
// Send out USB serial
|
// Send out USB serial
|
||||||
CDC_Transmit_FS(buf, len);
|
CDC_Transmit_FS(data, len);
|
||||||
// if (res == USBD_OK)
|
// if (res == USBD_OK)
|
||||||
// logbuflen = 0;
|
// logbuflen = 0;
|
||||||
#endif
|
#endif
|
||||||
|
@ -17,7 +17,7 @@ int __errno = 0;
|
|||||||
|
|
||||||
void rng_get_bytes(uint8_t * dst, size_t sz)
|
void rng_get_bytes(uint8_t * dst, size_t sz)
|
||||||
{
|
{
|
||||||
uint8_t r[8];
|
uint8_t r[4];
|
||||||
unsigned int i,j;
|
unsigned int i,j;
|
||||||
for (i = 0; i < sz; i += 4)
|
for (i = 0; i < sz; i += 4)
|
||||||
{
|
{
|
||||||
@ -33,7 +33,7 @@ void rng_get_bytes(uint8_t * dst, size_t sz)
|
|||||||
|
|
||||||
for (j = 0; j < 4; j++)
|
for (j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
if ((i + j) > sz)
|
if ((i + j) >= sz)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,183 +0,0 @@
|
|||||||
/*
|
|
||||||
*****************************************************************************
|
|
||||||
**
|
|
||||||
|
|
||||||
** File : LinkerScript.ld
|
|
||||||
**
|
|
||||||
** Abstract : Linker script for STM32L432KCUx Device with
|
|
||||||
** 256KByte FLASH, 64KByte RAM
|
|
||||||
**
|
|
||||||
** Set heap size, stack size and stack location according
|
|
||||||
** to application requirements.
|
|
||||||
**
|
|
||||||
** Set memory bank area and size if external memory is used.
|
|
||||||
**
|
|
||||||
** Target : STMicroelectronics STM32
|
|
||||||
**
|
|
||||||
**
|
|
||||||
** Distribution: The file is distributed as is, without any warranty
|
|
||||||
** of any kind.
|
|
||||||
**
|
|
||||||
** (c)Copyright Ac6.
|
|
||||||
** You may use this file as-is or modify it according to the needs of your
|
|
||||||
** project. Distribution of this file (unmodified or modified) is not
|
|
||||||
** permitted. Ac6 permit registered System Workbench for MCU users the
|
|
||||||
** rights to distribute the assembled, compiled & linked contents of this
|
|
||||||
** file as part of an application binary file, provided that it is built
|
|
||||||
** using the System Workbench for MCU toolchain.
|
|
||||||
**
|
|
||||||
*****************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Entry Point */
|
|
||||||
ENTRY(Reset_Handler)
|
|
||||||
|
|
||||||
/* Highest address of the user mode stack */
|
|
||||||
_estack = 0x20010000; /* end of RAM */
|
|
||||||
/* Generate a link error if heap and stack don't fit into RAM */
|
|
||||||
_Min_Heap_Size = 0x200; /* required amount of heap */
|
|
||||||
_Min_Stack_Size = 0x400; /* required amount of stack */
|
|
||||||
|
|
||||||
/* Specify the memory areas */
|
|
||||||
MEMORY
|
|
||||||
{
|
|
||||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
|
|
||||||
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 256K
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Define output sections */
|
|
||||||
SECTIONS
|
|
||||||
{
|
|
||||||
/* The startup code goes first into FLASH */
|
|
||||||
.isr_vector :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
KEEP(*(.isr_vector)) /* Startup code */
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
/* The program code and other data goes into FLASH */
|
|
||||||
.text :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
*(.text) /* .text sections (code) */
|
|
||||||
*(.text*) /* .text* sections (code) */
|
|
||||||
*(.glue_7) /* glue arm to thumb code */
|
|
||||||
*(.glue_7t) /* glue thumb to arm code */
|
|
||||||
*(.eh_frame)
|
|
||||||
|
|
||||||
KEEP (*(.init))
|
|
||||||
KEEP (*(.fini))
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
|
||||||
_etext = .; /* define a global symbols at end of code */
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
/* Constant data goes into FLASH */
|
|
||||||
.rodata :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
|
||||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
.ARM.extab :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
.ARM : {
|
|
||||||
. = ALIGN(8);
|
|
||||||
__exidx_start = .;
|
|
||||||
*(.ARM.exidx*)
|
|
||||||
__exidx_end = .;
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
.preinit_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
|
||||||
KEEP (*(.preinit_array*))
|
|
||||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
.init_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__init_array_start = .);
|
|
||||||
KEEP (*(SORT(.init_array.*)))
|
|
||||||
KEEP (*(.init_array*))
|
|
||||||
PROVIDE_HIDDEN (__init_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
.fini_array :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
|
||||||
KEEP (*(SORT(.fini_array.*)))
|
|
||||||
KEEP (*(.fini_array*))
|
|
||||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >FLASH
|
|
||||||
|
|
||||||
/* used by the startup to initialize data */
|
|
||||||
_sidata = LOADADDR(.data);
|
|
||||||
|
|
||||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
|
||||||
.data :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
_sdata = .; /* create a global symbol at data start */
|
|
||||||
*(.data) /* .data sections */
|
|
||||||
*(.data*) /* .data* sections */
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
|
||||||
_edata = .; /* define a global symbol at data end */
|
|
||||||
} >RAM AT> FLASH
|
|
||||||
|
|
||||||
|
|
||||||
/* Uninitialized data section */
|
|
||||||
. = ALIGN(4);
|
|
||||||
.bss :
|
|
||||||
{
|
|
||||||
/* This is used by the startup in order to initialize the .bss secion */
|
|
||||||
_sbss = .; /* define a global symbol at bss start */
|
|
||||||
__bss_start__ = _sbss;
|
|
||||||
*(.bss)
|
|
||||||
*(.bss*)
|
|
||||||
*(COMMON)
|
|
||||||
|
|
||||||
. = ALIGN(4);
|
|
||||||
_ebss = .; /* define a global symbol at bss end */
|
|
||||||
__bss_end__ = _ebss;
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
|
||||||
._user_heap_stack :
|
|
||||||
{
|
|
||||||
. = ALIGN(8);
|
|
||||||
PROVIDE ( end = . );
|
|
||||||
PROVIDE ( _end = . );
|
|
||||||
. = . + _Min_Heap_Size;
|
|
||||||
. = . + _Min_Stack_Size;
|
|
||||||
. = ALIGN(8);
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Remove information from the standard libraries */
|
|
||||||
/DISCARD/ :
|
|
||||||
{
|
|
||||||
libc.a ( * )
|
|
||||||
libm.a ( * )
|
|
||||||
libgcc.a ( * )
|
|
||||||
}
|
|
||||||
|
|
||||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
2
tinycbor
2
tinycbor
Submodule tinycbor updated: c9059d9e33...878eb01b96
1829
tools/ctap_test.py
1829
tools/ctap_test.py
File diff suppressed because it is too large
Load Diff
65
tools/gadgetfs/Makefile
Normal file
65
tools/gadgetfs/Makefile
Normal 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
|
||||||
|
|
||||||
|
|
58
tools/testing/main.py
Normal file
58
tools/testing/main.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2019 SoloKeys Developers
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||||
|
# http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||||
|
# http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||||
|
# copied, modified, or distributed except according to those terms.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Script for testing correctness of CTAP2/CTAP1 security token
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from solo.fido2 import force_udp_backend
|
||||||
|
from tests import Tester, FIDO2Tests, U2FTests, HIDTests, SoloTests
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: %s [sim] <[u2f]|[fido2]|[rk]|[hid]|[ping]>")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
t = Tester()
|
||||||
|
t.set_user_count(3)
|
||||||
|
|
||||||
|
if "sim" in sys.argv:
|
||||||
|
print("Using UDP backend.")
|
||||||
|
force_udp_backend()
|
||||||
|
t.set_sim(True)
|
||||||
|
t.set_user_count(10)
|
||||||
|
|
||||||
|
t.find_device()
|
||||||
|
|
||||||
|
if "solo" in sys.argv:
|
||||||
|
SoloTests(t).run()
|
||||||
|
|
||||||
|
if "u2f" in sys.argv:
|
||||||
|
U2FTests(t).run()
|
||||||
|
|
||||||
|
if "fido2" in sys.argv:
|
||||||
|
# t.test_fido2()
|
||||||
|
FIDO2Tests(t).run()
|
||||||
|
|
||||||
|
# hid tests are a bit invasive and should be done last
|
||||||
|
if "hid" in sys.argv:
|
||||||
|
HIDTests(t).run()
|
||||||
|
|
||||||
|
if "bootloader" in sys.argv:
|
||||||
|
if t.is_sim:
|
||||||
|
raise RuntimeError("Cannot test bootloader in simulation yet.")
|
||||||
|
# print("Put device in bootloader mode and then hit enter")
|
||||||
|
# input()
|
||||||
|
# t.test_bootloader()
|
||||||
|
|
||||||
|
# t.test_responses()
|
||||||
|
# t.test_fido2_brute_force()
|
11
tools/testing/tests/__init__.py
Normal file
11
tools/testing/tests/__init__.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from . import fido2
|
||||||
|
from . import hid
|
||||||
|
from . import solo
|
||||||
|
from . import u2f
|
||||||
|
from . import tester
|
||||||
|
|
||||||
|
FIDO2Tests = fido2.FIDO2Tests
|
||||||
|
HIDTests = hid.HIDTests
|
||||||
|
U2FTests = u2f.U2FTests
|
||||||
|
SoloTests = solo.SoloTests
|
||||||
|
Tester = tester.Tester
|
1278
tools/testing/tests/fido2.py
Normal file
1278
tools/testing/tests/fido2.py
Normal file
File diff suppressed because it is too large
Load Diff
252
tools/testing/tests/hid.py
Normal file
252
tools/testing/tests/hid.py
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
import sys, os, time
|
||||||
|
from binascii import hexlify
|
||||||
|
|
||||||
|
from fido2.hid import CTAPHID
|
||||||
|
from fido2.ctap import CtapError
|
||||||
|
|
||||||
|
from .tester import Tester, Test
|
||||||
|
|
||||||
|
|
||||||
|
class HIDTests(Tester):
|
||||||
|
def __init__(self, tester=None):
|
||||||
|
super().__init__(tester)
|
||||||
|
self.check_timeouts = False
|
||||||
|
|
||||||
|
def set_check_timeouts(self, en):
|
||||||
|
self.check_timeouts = en
|
||||||
|
|
||||||
|
def run(self,):
|
||||||
|
self.test_long_ping()
|
||||||
|
self.test_hid(self.check_timeouts)
|
||||||
|
|
||||||
|
def test_long_ping(self):
|
||||||
|
amt = 1000
|
||||||
|
pingdata = os.urandom(amt)
|
||||||
|
with Test("Send %d byte ping" % amt):
|
||||||
|
try:
|
||||||
|
t1 = time.time() * 1000
|
||||||
|
r = self.send_data(CTAPHID.PING, pingdata)
|
||||||
|
t2 = time.time() * 1000
|
||||||
|
delt = t2 - t1
|
||||||
|
# if (delt < 140 ):
|
||||||
|
# raise RuntimeError('Fob is too fast (%d ms)' % delt)
|
||||||
|
if delt > 555 * (amt / 1000):
|
||||||
|
raise RuntimeError("Fob is too slow (%d ms)" % delt)
|
||||||
|
if r != pingdata:
|
||||||
|
raise ValueError("Ping data not echo'd")
|
||||||
|
except CtapError:
|
||||||
|
raise RuntimeError("ping failed")
|
||||||
|
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
def test_hid(self, check_timeouts=False):
|
||||||
|
if check_timeouts:
|
||||||
|
with Test("idle"):
|
||||||
|
try:
|
||||||
|
cmd, resp = self.recv_raw()
|
||||||
|
except socket.timeout:
|
||||||
|
pass
|
||||||
|
|
||||||
|
with Test("init"):
|
||||||
|
r = self.send_data(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11")
|
||||||
|
|
||||||
|
with Test("100 byte ping"):
|
||||||
|
pingdata = os.urandom(100)
|
||||||
|
try:
|
||||||
|
r = self.send_data(CTAPHID.PING, pingdata)
|
||||||
|
if r != pingdata:
|
||||||
|
raise ValueError("Ping data not echo'd")
|
||||||
|
except CtapError as e:
|
||||||
|
print("100 byte Ping failed:", e)
|
||||||
|
raise RuntimeError("ping failed")
|
||||||
|
|
||||||
|
self.test_long_ping()
|
||||||
|
|
||||||
|
with Test("Wink"):
|
||||||
|
r = self.send_data(CTAPHID.WINK, "")
|
||||||
|
|
||||||
|
with Test("CBOR msg with no data"):
|
||||||
|
try:
|
||||||
|
r = self.send_data(CTAPHID.CBOR, "")
|
||||||
|
if len(r) > 1 or r[0] == 0:
|
||||||
|
raise RuntimeError("Cbor is supposed to have payload")
|
||||||
|
except CtapError as e:
|
||||||
|
assert e.code == CtapError.ERR.INVALID_LENGTH
|
||||||
|
|
||||||
|
with Test("No data in U2F msg"):
|
||||||
|
try:
|
||||||
|
r = self.send_data(CTAPHID.MSG, "")
|
||||||
|
print(hexlify(r))
|
||||||
|
if len(r) > 2:
|
||||||
|
raise RuntimeError("MSG is supposed to have payload")
|
||||||
|
except CtapError as e:
|
||||||
|
assert e.code == CtapError.ERR.INVALID_LENGTH
|
||||||
|
|
||||||
|
with Test("Use init command to resync"):
|
||||||
|
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||||
|
|
||||||
|
with Test("Invalid HID command"):
|
||||||
|
try:
|
||||||
|
r = self.send_data(0x66, "")
|
||||||
|
raise RuntimeError("Invalid command did not return error")
|
||||||
|
except CtapError as e:
|
||||||
|
assert e.code == CtapError.ERR.INVALID_COMMAND
|
||||||
|
|
||||||
|
with Test("Sending packet with too large of a length."):
|
||||||
|
self.send_raw("\x81\x1d\xba\x00")
|
||||||
|
cmd, resp = self.recv_raw()
|
||||||
|
Tester.check_error(resp, CtapError.ERR.INVALID_LENGTH)
|
||||||
|
|
||||||
|
r = self.send_data(CTAPHID.PING, "\x44" * 200)
|
||||||
|
with Test("Sending packets that skip a sequence number."):
|
||||||
|
self.send_raw("\x81\x04\x90")
|
||||||
|
self.send_raw("\x00")
|
||||||
|
self.send_raw("\x01")
|
||||||
|
# skip 2
|
||||||
|
self.send_raw("\x03")
|
||||||
|
cmd, resp = self.recv_raw()
|
||||||
|
Tester.check_error(resp, CtapError.ERR.INVALID_SEQ)
|
||||||
|
|
||||||
|
with Test("Resync and send ping"):
|
||||||
|
try:
|
||||||
|
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||||
|
pingdata = os.urandom(100)
|
||||||
|
r = self.send_data(CTAPHID.PING, pingdata)
|
||||||
|
if r != pingdata:
|
||||||
|
raise ValueError("Ping data not echo'd")
|
||||||
|
except CtapError as e:
|
||||||
|
raise RuntimeError("resync fail: ", e)
|
||||||
|
|
||||||
|
with Test("Send ping and abort it"):
|
||||||
|
self.send_raw("\x81\x04\x00")
|
||||||
|
self.send_raw("\x00")
|
||||||
|
self.send_raw("\x01")
|
||||||
|
try:
|
||||||
|
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||||
|
except CtapError as e:
|
||||||
|
raise RuntimeError("resync fail: ", e)
|
||||||
|
|
||||||
|
with Test("Send ping and abort it with different cid, expect timeout"):
|
||||||
|
oldcid = self.cid()
|
||||||
|
newcid = "\x11\x22\x33\x44"
|
||||||
|
self.send_raw("\x81\x10\x00")
|
||||||
|
self.send_raw("\x00")
|
||||||
|
self.send_raw("\x01")
|
||||||
|
self.set_cid(newcid)
|
||||||
|
self.send_raw(
|
||||||
|
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88"
|
||||||
|
) # init from different cid
|
||||||
|
print("wait for init response")
|
||||||
|
cmd, r = self.recv_raw() # init response
|
||||||
|
assert cmd == 0x86
|
||||||
|
self.set_cid(oldcid)
|
||||||
|
if check_timeouts:
|
||||||
|
# print('wait for timeout')
|
||||||
|
cmd, r = self.recv_raw() # timeout response
|
||||||
|
assert cmd == 0xBF
|
||||||
|
|
||||||
|
with Test("Test timeout"):
|
||||||
|
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||||
|
t1 = time.time() * 1000
|
||||||
|
self.send_raw("\x81\x04\x00")
|
||||||
|
self.send_raw("\x00")
|
||||||
|
self.send_raw("\x01")
|
||||||
|
cmd, r = self.recv_raw() # timeout response
|
||||||
|
t2 = time.time() * 1000
|
||||||
|
delt = t2 - t1
|
||||||
|
assert cmd == 0xBF
|
||||||
|
assert r[0] == CtapError.ERR.TIMEOUT
|
||||||
|
assert delt < 1000 and delt > 400
|
||||||
|
|
||||||
|
with Test("Test not cont"):
|
||||||
|
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||||
|
self.send_raw("\x81\x04\x00")
|
||||||
|
self.send_raw("\x00")
|
||||||
|
self.send_raw("\x01")
|
||||||
|
self.send_raw("\x81\x10\x00") # init packet
|
||||||
|
cmd, r = self.recv_raw() # timeout response
|
||||||
|
assert cmd == 0xBF
|
||||||
|
assert r[0] == CtapError.ERR.INVALID_SEQ
|
||||||
|
|
||||||
|
if check_timeouts:
|
||||||
|
with Test("Check random cont ignored"):
|
||||||
|
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||||
|
self.send_raw("\x01\x10\x00")
|
||||||
|
try:
|
||||||
|
cmd, r = self.recv_raw() # timeout response
|
||||||
|
except socket.timeout:
|
||||||
|
pass
|
||||||
|
|
||||||
|
with Test("Check busy"):
|
||||||
|
t1 = time.time() * 1000
|
||||||
|
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||||
|
oldcid = self.cid()
|
||||||
|
newcid = "\x11\x22\x33\x44"
|
||||||
|
self.send_raw("\x81\x04\x00")
|
||||||
|
self.set_cid(newcid)
|
||||||
|
self.send_raw("\x81\x04\x00")
|
||||||
|
cmd, r = self.recv_raw() # busy response
|
||||||
|
t2 = time.time() * 1000
|
||||||
|
assert t2 - t1 < 100
|
||||||
|
assert cmd == 0xBF
|
||||||
|
assert r[0] == CtapError.ERR.CHANNEL_BUSY
|
||||||
|
|
||||||
|
self.set_cid(oldcid)
|
||||||
|
cmd, r = self.recv_raw() # timeout response
|
||||||
|
assert cmd == 0xBF
|
||||||
|
assert r[0] == CtapError.ERR.TIMEOUT
|
||||||
|
|
||||||
|
with Test("Check busy interleaved"):
|
||||||
|
cid1 = "\x11\x22\x33\x44"
|
||||||
|
cid2 = "\x01\x22\x33\x44"
|
||||||
|
self.set_cid(cid2)
|
||||||
|
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||||
|
self.set_cid(cid1)
|
||||||
|
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||||
|
self.send_raw("\x81\x00\x63") # echo 99 bytes first channel
|
||||||
|
|
||||||
|
self.set_cid(cid2) # send ping on 2nd channel
|
||||||
|
self.send_raw("\x81\x00\x63")
|
||||||
|
Tester.delay(0.1)
|
||||||
|
self.send_raw("\x00")
|
||||||
|
|
||||||
|
cmd, r = self.recv_raw() # busy response
|
||||||
|
|
||||||
|
self.set_cid(cid1) # finish 1st channel ping
|
||||||
|
self.send_raw("\x00")
|
||||||
|
|
||||||
|
self.set_cid(cid2)
|
||||||
|
|
||||||
|
assert cmd == 0xBF
|
||||||
|
assert r[0] == CtapError.ERR.CHANNEL_BUSY
|
||||||
|
|
||||||
|
self.set_cid(cid1)
|
||||||
|
cmd, r = self.recv_raw() # ping response
|
||||||
|
assert cmd == 0x81
|
||||||
|
assert len(r) == 0x63
|
||||||
|
|
||||||
|
if check_timeouts:
|
||||||
|
with Test("Test idle, wait for timeout"):
|
||||||
|
sys.stdout.flush()
|
||||||
|
try:
|
||||||
|
cmd, resp = self.recv_raw()
|
||||||
|
except socket.timeout:
|
||||||
|
pass
|
||||||
|
|
||||||
|
with Test("Test cid 0 is invalid"):
|
||||||
|
self.set_cid("\x00\x00\x00\x00")
|
||||||
|
self.send_raw(
|
||||||
|
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\x00\x00\x00\x00"
|
||||||
|
)
|
||||||
|
cmd, r = self.recv_raw() # timeout
|
||||||
|
assert cmd == 0xBF
|
||||||
|
assert r[0] == CtapError.ERR.INVALID_CHANNEL
|
||||||
|
|
||||||
|
with Test("Test invalid broadcast cid use"):
|
||||||
|
self.set_cid("\xff\xff\xff\xff")
|
||||||
|
self.send_raw(
|
||||||
|
"\x81\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\xff\xff\xff\xff"
|
||||||
|
)
|
||||||
|
cmd, r = self.recv_raw() # timeout
|
||||||
|
assert cmd == 0xBF
|
||||||
|
assert r[0] == CtapError.ERR.INVALID_CHANNEL
|
83
tools/testing/tests/solo.py
Normal file
83
tools/testing/tests/solo.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
from solo.client import SoloClient
|
||||||
|
from solo.commands import SoloExtension
|
||||||
|
|
||||||
|
from fido2.ctap1 import ApduError
|
||||||
|
from fido2.utils import sha256
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
req = SoloClient.format_request(SoloExtension.version, 0, b"A" * 16)
|
||||||
|
a = sc.ctap2.get_assertion(
|
||||||
|
sc.host, b"B" * 32, [{"id": req, "type": "public-key"}]
|
||||||
|
)
|
||||||
|
|
||||||
|
with Test("Test custom command returned valid assertion"):
|
||||||
|
assert a.auth_data.rp_id_hash == sha256(sc.host.encode("utf8"))
|
||||||
|
assert a.credential["id"] == req
|
||||||
|
assert (a.auth_data.flags & 0x5) == 0x5
|
||||||
|
|
||||||
|
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
|
204
tools/testing/tests/tester.py
Normal file
204
tools/testing/tests/tester.py
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
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)
|
121
tools/testing/tests/u2f.py
Normal file
121
tools/testing/tests/u2f.py
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
from fido2.ctap1 import CTAP1, ApduError, APDU
|
||||||
|
from fido2.utils import sha256
|
||||||
|
from fido2.client import _call_polling
|
||||||
|
|
||||||
|
from .tester import Tester, Test
|
||||||
|
|
||||||
|
|
||||||
|
class U2FTests(Tester):
|
||||||
|
def __init__(self, tester=None):
|
||||||
|
super().__init__(tester)
|
||||||
|
|
||||||
|
def run(self,):
|
||||||
|
self.test_u2f()
|
||||||
|
|
||||||
|
def register(self, chal, appid):
|
||||||
|
reg_data = _call_polling(0.25, None, None, self.ctap1.register, chal, appid)
|
||||||
|
return reg_data
|
||||||
|
|
||||||
|
def authenticate(self, chal, appid, key_handle, check_only=False):
|
||||||
|
auth_data = _call_polling(
|
||||||
|
0.25,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
self.ctap1.authenticate,
|
||||||
|
chal,
|
||||||
|
appid,
|
||||||
|
key_handle,
|
||||||
|
check_only=check_only,
|
||||||
|
)
|
||||||
|
return auth_data
|
||||||
|
|
||||||
|
def test_u2f(self,):
|
||||||
|
chal = sha256(b"AAA")
|
||||||
|
appid = sha256(b"BBB")
|
||||||
|
lastc = 0
|
||||||
|
|
||||||
|
regs = []
|
||||||
|
|
||||||
|
with Test("Check version"):
|
||||||
|
assert self.ctap1.get_version() == "U2F_V2"
|
||||||
|
|
||||||
|
with Test("Check bad INS"):
|
||||||
|
try:
|
||||||
|
self.ctap1.send_apdu(0, 0, 0, 0, b"")
|
||||||
|
except ApduError as e:
|
||||||
|
assert e.code == 0x6D00
|
||||||
|
|
||||||
|
with Test("Check bad CLA"):
|
||||||
|
try:
|
||||||
|
self.ctap1.send_apdu(1, CTAP1.INS.VERSION, 0, 0, b"abc")
|
||||||
|
except ApduError as e:
|
||||||
|
assert e.code == 0x6E00
|
||||||
|
|
||||||
|
for i in range(0, self.user_count):
|
||||||
|
with Test(
|
||||||
|
"U2F reg + auth %d/%d (count: %02x)" % (i + 1, self.user_count, lastc)
|
||||||
|
):
|
||||||
|
reg = self.register(chal, appid)
|
||||||
|
reg.verify(appid, chal)
|
||||||
|
auth = self.authenticate(chal, appid, reg.key_handle)
|
||||||
|
auth.verify(appid, chal, reg.public_key)
|
||||||
|
|
||||||
|
regs.append(reg)
|
||||||
|
# check endianness
|
||||||
|
if lastc:
|
||||||
|
assert (auth.counter - lastc) < 10
|
||||||
|
lastc = auth.counter
|
||||||
|
if lastc > 0x80000000:
|
||||||
|
print("WARNING: counter is unusually high: %04x" % lastc)
|
||||||
|
assert 0
|
||||||
|
|
||||||
|
for i in range(0, self.user_count):
|
||||||
|
with Test(
|
||||||
|
"Checking previous registration %d/%d" % (i + 1, self.user_count)
|
||||||
|
):
|
||||||
|
auth = self.authenticate(chal, appid, regs[i].key_handle)
|
||||||
|
auth.verify(appid, chal, regs[i].public_key)
|
||||||
|
|
||||||
|
print("Check that all previous credentials are registered...")
|
||||||
|
for i in range(0, self.user_count):
|
||||||
|
with Test("Check that previous credential %d is registered" % i):
|
||||||
|
try:
|
||||||
|
auth = self.ctap1.authenticate(
|
||||||
|
chal, appid, regs[i].key_handle, check_only=True
|
||||||
|
)
|
||||||
|
except ApduError as e:
|
||||||
|
# Indicates that key handle is registered
|
||||||
|
assert e.code == APDU.USE_NOT_SATISFIED
|
||||||
|
|
||||||
|
with Test("Check an incorrect key handle is not registered"):
|
||||||
|
kh = bytearray(regs[0].key_handle)
|
||||||
|
kh[0] = kh[0] ^ (0x40)
|
||||||
|
try:
|
||||||
|
self.ctap1.authenticate(chal, appid, kh, check_only=True)
|
||||||
|
assert 0
|
||||||
|
except ApduError as e:
|
||||||
|
assert e.code == APDU.WRONG_DATA
|
||||||
|
|
||||||
|
with Test("Try to sign with incorrect key handle"):
|
||||||
|
try:
|
||||||
|
self.ctap1.authenticate(chal, appid, kh)
|
||||||
|
assert 0
|
||||||
|
except ApduError as e:
|
||||||
|
assert e.code == APDU.WRONG_DATA
|
||||||
|
|
||||||
|
with Test("Try to sign using an incorrect keyhandle length"):
|
||||||
|
try:
|
||||||
|
kh = regs[0].key_handle
|
||||||
|
self.ctap1.authenticate(chal, appid, kh[: len(kh) // 2])
|
||||||
|
assert 0
|
||||||
|
except ApduError as e:
|
||||||
|
assert e.code == APDU.WRONG_DATA
|
||||||
|
|
||||||
|
with Test("Try to sign using an incorrect appid"):
|
||||||
|
badid = bytearray(appid)
|
||||||
|
badid[0] = badid[0] ^ (0x40)
|
||||||
|
try:
|
||||||
|
auth = self.ctap1.authenticate(chal, badid, regs[0].key_handle)
|
||||||
|
assert 0
|
||||||
|
except ApduError as e:
|
||||||
|
assert e.code == APDU.WRONG_DATA
|
12
tools/testing/tests/util.py
Normal file
12
tools/testing/tests/util.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
|
def shannon_entropy(data):
|
||||||
|
s = 0.0
|
||||||
|
total = len(data)
|
||||||
|
for x in range(0, 256):
|
||||||
|
freq = data.count(x)
|
||||||
|
p = freq / total
|
||||||
|
if p > 0:
|
||||||
|
s -= p * math.log2(p)
|
||||||
|
return s
|
@ -13,7 +13,7 @@ SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="ua
|
|||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess"
|
||||||
|
|
||||||
# ST DFU access
|
# ST DFU access
|
||||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", TAG+="uaccess"
|
SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", TAG+="uaccess"
|
||||||
|
|
||||||
# U2F Zero
|
# U2F Zero
|
||||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess"
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess"
|
||||||
|
@ -13,7 +13,7 @@ SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", MODE="06
|
|||||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", MODE="0660", GROUP="plugdev"
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", MODE="0660", GROUP="plugdev"
|
||||||
|
|
||||||
# ST DFU access
|
# ST DFU access
|
||||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="0660", GROUP="plugdev"
|
SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="0660", GROUP="plugdev"
|
||||||
|
|
||||||
# U2F Zero
|
# U2F Zero
|
||||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", MODE="0660", GROUP="plugdev"
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", MODE="0660", GROUP="plugdev"
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
setup: install activate
|
setup: install activate
|
||||||
legacy-setup: install-legacy activate
|
legacy-setup: install-legacy activate
|
||||||
|
|
||||||
|
# Symlinks can be setup, we don't officially supply any
|
||||||
# symlinks: install-symlinks activate
|
# symlinks: install-symlinks activate
|
||||||
|
|
||||||
RULES_PATH=/etc/udev/rules.d
|
RULES_PATH=/etc/udev/rules.d
|
||||||
@ -19,10 +21,10 @@ activate:
|
|||||||
sudo udevadm trigger
|
sudo udevadm trigger
|
||||||
|
|
||||||
install:
|
install:
|
||||||
sudo ln -sf $(PWD)/70-solokeys-access.rules ${RULES_PATH}/70-solokeys-access.rules
|
sudo cp $(PWD)/70-solokeys-access.rules ${RULES_PATH}/70-solokeys-access.rules
|
||||||
|
|
||||||
install-legacy:
|
install-legacy:
|
||||||
sudo ln -sf $(PWD)/70-solokeys-legacy-access.rules ${RULES_PATH}/70-solokeys-access.rules
|
sudo cp $(PWD)/70-solokeys-legacy-access.rules ${RULES_PATH}/70-solokeys-access.rules
|
||||||
|
|
||||||
# install-symlinks:
|
# install-symlinks:
|
||||||
# sudo cp $(PWD)/71-solokeys-symlinks.rules ${RULES_PATH}/71-solokeys-symlinks.rules
|
# sudo cp $(PWD)/71-solokeys-symlinks.rules ${RULES_PATH}/71-solokeys-symlinks.rules
|
||||||
|
14
udev/README.md
Normal file
14
udev/README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
This is for Linux systems only.
|
||||||
|
|
||||||
|
To install the official SoloKeys udev rules, allowing access to your key, run
|
||||||
|
|
||||||
|
```
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
This should work assuming your system is reasonably up-to-date. If not, try
|
||||||
|
|
||||||
|
```
|
||||||
|
make install-legacy
|
||||||
|
```
|
||||||
|
|
28
web/.key
28
web/.key
@ -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-----
|
|
@ -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>
|
|
803
web/js/aes.js
803
web/js/aes.js
@ -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);
|
|
8812
web/js/elliptic.js
8812
web/js/elliptic.js
File diff suppressed because it is too large
Load Diff
@ -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
|
|
9
web/js/sha256.min.js
vendored
9
web/js/sha256.min.js
vendored
File diff suppressed because one or more lines are too long
@ -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);
|
|
||||||
});
|
|
||||||
};
|
|
1467
web/js/wallet.js
1467
web/js/wallet.js
File diff suppressed because it is too large
Load Diff
@ -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-----
|
|
@ -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-----
|
|
@ -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")
|
|
@ -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()
|
|
Reference in New Issue
Block a user