diff --git a/.gitignore b/.gitignore index ff38151..16fb66f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ bin/ +dev-env/ + +ledger/ + debug/ dep/ @@ -13,5 +17,4 @@ src/glyphs\.h usbtool/ -src/txnTypeLists.c .vscode/ diff --git a/ArdorIconNanoS.gif b/ArdorIconNanoS.gif deleted file mode 100644 index 3332de3..0000000 Binary files a/ArdorIconNanoS.gif and /dev/null differ diff --git a/ArdorIconNanoX.gif b/ArdorIconNanoX.gif deleted file mode 100644 index 366b7d5..0000000 Binary files a/ArdorIconNanoX.gif and /dev/null differ diff --git a/Makefile b/Makefile index 8130199..f660841 100755 --- a/Makefile +++ b/Makefile @@ -16,8 +16,8 @@ # limitations under the License. #******************************************************************************* -#Defines -DEVEL = 0#This means we are in DEBUG mode, change this up when releasing in production +# DEVEL = 1 we are in DEBUG mode, change this up when releasing in production +DEVEL = 1 #####################################3 @@ -27,35 +27,17 @@ endif include $(BOLOS_SDK)/Makefile.defines -ifndef COIN -COIN=ardor -endif - -ifeq ($(COIN),ardor) - APPNAME = Ardor - DEFINES = "PATH_PREFIX={44|0x80000000,16754|0x80000000}" - PATH_PREFIX = "44'/16754'" - DEFINES += APP_PREFIX=\"ARDOR-\" +APPNAME = Burstcoin +DEFINES = "PATH_PREFIX={44|0x80000000,30|0x80000000}" +PATH_PREFIX = "44'/30'" +DEFINES += APP_PREFIX=\"BURST-\" - ifeq ($(TARGET_NAME),TARGET_NANOX) - ICONNAME = ArdorIconNanoX.gif - else - ICONNAME = ArdorIconNanoS.gif - endif -else ifeq ($(COIN),nxt) - APPNAME = NXT - DEFINES = "PATH_PREFIX={44|0x80000000,29|0x80000000}" - PATH_PREFIX = "44'/29'" - DEFINES += APP_PREFIX=\"NXT-\" - - ifeq ($(TARGET_NAME),TARGET_NANOX) - ICONNAME = NXTIconNanoX.gif - else - ICONNAME = NXTIconNanoS.gif - endif +ifeq ($(TARGET_NAME),TARGET_NANOX) + ICONNAME = icons/nanox_app_burst.gif else - $(error /!\ Coin "$(COIN)" not in list of allowed variants! Type "make listvariants" for variants list. Build non-default variant with "make COIN=") + ICONNAME = icons/nanos_app_burst.gif endif + $(info Building $(APPNAME) app...) ############ @@ -95,7 +77,7 @@ endif DEFINES += HAVE_UX_FLOW -APPVERSION_M = 1 +APPVERSION_M = 0 APPVERSION_N = 0 APPVERSION_P = 1 @@ -139,20 +121,12 @@ else DEFINES += PRINTF\(...\)= endif -AUTOGEN_SRC := src/txnTypeLists.c AUTOGEN_OBJ := $(AUTOGEN_SRC:src/%.c=obj/%.o) -SOURCE_FILES += $(AUTOGEN_SRC) - .PHONY: realclean clean all: default -$(AUTOGEN_OBJ): src/authAndSignTxn.c $(AUTOGEN_SRC) - -$(AUTOGEN_SRC): createTxnTypes.py txtypes.txt - python ./createTxnTypes.py > $@ - load: all python -m ledgerblue.loadApp $(APP_LOAD_PARAMS) @@ -199,5 +173,3 @@ include $(BOLOS_SDK)/Makefile.rules dep/%.d: %.c Makefile -listvariants: - @echo VARIANTS COIN ardor nxt diff --git a/NXTIconNanoS.gif b/NXTIconNanoS.gif deleted file mode 100644 index b869727..0000000 Binary files a/NXTIconNanoS.gif and /dev/null differ diff --git a/NXTIconNanoX.gif b/NXTIconNanoX.gif deleted file mode 100644 index a6d1cde..0000000 Binary files a/NXTIconNanoX.gif and /dev/null differ diff --git a/README.md b/README.md index b65125d..0b26d95 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,37 @@ -# Ledger App for Ardor +# Ledger App for Burst -This is the official [Ardor](https://www.jelurida.com/ardor) ledger wallet app for the Ledger Nano S and X devices +This is the official [Burst](https://burst-coin.org) ledger wallet app for the Ledger Nano S and X devices. +Initially forked from app-ledger-ardor but transaction parsing and signing were rewritten. ## Documentation -[Ardor Wiki](https://ardordocs.jelurida.com/Connect_Ledger_Nano_S_to_your_Ardor_Wallet), [Ledger Documentation Hub](https://ledger.readthedocs.io/en/latest/) +[Burst Wiki](https://burstwiki.org/en/), [Ledger Documentation Hub](https://ledger.readthedocs.io/en/latest/) ## Developer Resources -### Enable Log Messages +### Prepare the environment -To turn on logging on the Ledger app -1. Install the [debug firmware](https://ledger.readthedocs.io/en/latest/userspace/debugging.html) -2. Enable debugging in the makefile (DEVEL = 1) - make sure not to commit this change -3. Execute `make clean` and then `make load` to generate the source code for all the PRINTF statements +First, **update your ledger to the latest firmware**. -### Switch Between Target Builds +After that, install prerequisite packages: -In order to build the Nano S or Nano X version you just need to make sure the `BOLOS_SDK` environment variable points to the corresponding SDK. +```bash +sudo apt install python3-venv python3-dev libudev-dev libusb-1.0-0-dev libtinfo.so.5 +``` -Make sure you rebuild the whole project by executing `make clean` and then `make load`. +Now use the `prepare-devenv.sh` script to prepare a local development environment with the right target (`s` or `x`). -### Avoid Numeric Underflow +```bash +# (x or s, depending on your device) +source prepare-devenv.sh s +``` -Be careful not to underflow unsigned numeric types, for example: +### Enable Log Messages (optional) -`n = (dataLength - 32) / sizeof(uint32_t);` - -This line would underflow in case the `dataLength` variable is smaller than 32 which might lead to disaster -so please review carefully all substraction operations. +To turn on logging on the Ledger app +1. Install the [debug firmware](https://ledger.readthedocs.io/en/latest/userspace/debugging.html) +2. Enable debugging in the makefile (DEVEL = 1) - make sure not to commit this change +3. Execute `make clean` and then `make load` to generate the source code for all the PRINTF statements ### Zero Tolerance for Compilation Warnings @@ -39,7 +42,7 @@ since they are externally imported. ### State Cleaning -Since we use a union data type for command handlers state (`states_t` in `ardor.h`) to save memory, make sure to **clear this state** +Since we use a union data type for command handlers state (`states_t` in `burst.h`) to save memory, make sure to **clear this state** to avoid some attack vectors. This is done by passing `true` in the `isLastCommandDifferent` parameter of the handler function. In this case the handler has @@ -53,17 +56,9 @@ Do not include statement for C source code inside other C source code to prevent Store constants and hardcoded values in config.h -### Transaction Types - -The `txnTypesList.c` source file is autogenerated by the `createTxnTypes.py` script from the `txtypes.txt` file. This step is automatically handled by the makefile. - -`txtypes.txt` should be generated externally by the Ardor developers whenever they add a new transaction type. - -Changes to the `txtypes.txt` should be picked up by the make process and a new `txnTypesList.c` automatically generated. The `txnTypesList.c` is not deleted on `make clean` but you can use `make realclean` that cleans everything. - ### Code Flow -The code flow starts at ardor_main (`main.c`) which uses a global try/catch to prevent the app from crashing on error. +The code flow starts at burst_main (`main.c`) which uses a global try/catch to prevent the app from crashing on error. The code loops on io_exchange waiting for the next command buffer, then calling the appropriate handler function implemented in the different .c files. @@ -114,9 +109,9 @@ All return values for functions should be checked in every function. ## Key Derivation Algorithm -Ardor signatures are based on the EC-KCDSA over Curve25519 algorithm which is not supported natively by Ledger. +Burst signatures are based on the EC-KCDSA over Curve25519 algorithm which is not supported natively by Ledger. -To support standard BIP32 key derivation we implemented curve conversion for Ardor using the protocol +To support standard BIP32 key derivation we implemented curve conversion for Burst using the protocol [Yaffe-Bender HD key derivation for EC-KCDSA](https://www.jelurida.com/sites/default/files/kcdsa.pdf) Technically a public key is a Point (X,Y) on a curve C. X,Y are integers modulo some field F with a base point on the curve G. diff --git a/createTxnTypes.py b/createTxnTypes.py deleted file mode 100644 index dfcbe6d..0000000 --- a/createTxnTypes.py +++ /dev/null @@ -1,30 +0,0 @@ - - -appendageParseFunctionDict = {0x000b : 4, 0x00fc: 4} - - -def main(): - - with open("txtypes.txt", "r") as f: - - out = '//This is an auto generated file by createTxnTypes.py\n\n\n\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include "config.h"\n#include "ardor.h"\n\nconst txnType TXN_TYPES[] = {' - - lines = f.readlines() - - for line in lines: - txtype, txSubType, name = line.split(',') - - txtype = int(txtype) & 0xFF - txSubType = int(txSubType) & 0xFF - - parseFunction = appendageParseFunctionDict.get(txSubType * 256 + txtype , 0) - - out += '{' + '0x{:02x}{:02x},"{}",{}'.format(txSubType, txtype, name.rstrip(), parseFunction) + '},\n' - - - print(out[0:-2] + "};") - - print("\nconst uint8_t LEN_TXN_TYPES = {};".format(len(lines))) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/glyphs/icon_dashboard.gif b/glyphs/icon_dashboard.gif index 5c30551..33d9b0a 100644 Binary files a/glyphs/icon_dashboard.gif and b/glyphs/icon_dashboard.gif differ diff --git a/icons/nanos_app_burst.gif b/icons/nanos_app_burst.gif new file mode 100644 index 0000000..ed9c1fc Binary files /dev/null and b/icons/nanos_app_burst.gif differ diff --git a/icons/nanox_app_burst.gif b/icons/nanox_app_burst.gif new file mode 100644 index 0000000..6e8d7b9 Binary files /dev/null and b/icons/nanox_app_burst.gif differ diff --git a/nanos_1.gif b/nanos_1.gif deleted file mode 100644 index 3332de3..0000000 Binary files a/nanos_1.gif and /dev/null differ diff --git a/prepare-devenv.sh b/prepare-devenv.sh new file mode 100644 index 0000000..6e8fe7e --- /dev/null +++ b/prepare-devenv.sh @@ -0,0 +1,79 @@ +# shellcheck disable=SC1091,SC2155 + +# SOURCE THIS FILE +# . prepare-devenv blue|s|x + +if [ $# -ne 1 ]; then + echo "Possible options: blue, s or x" + return +elif [[ $1 == "-h" ]]; then + echo "Possible options: blue, s or x" + return +elif [[ $1 != "blue" ]] && [[ $1 != "s" ]] && [[ $1 != "x" ]]; then + echo "Possible options: blue, s or x" + return +fi + +if [[ $(dpkg-query -s python3-venv 2>&1) == *'is not installed'* ]]; then + printf "\nPackage python3-venv is missing.\nOn Debian-like distros, run:\n\napt install python3-venv\n\n" + return +fi + +if [[ $(cat /etc/udev/rules.d/20-hw1.rules) == *'ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0004"'* ]]; then + printf "\nMissing udev rules. Please refer to https://support.ledger.com/hc/en-us/articles/115005165269-Fix-connection-issues\n\n" + return +fi + + +if [ ! -d dev-env ]; then + mkdir dev-env + mkdir dev-env/SDK + mkdir dev-env/CC + mkdir dev-env/CC/others + mkdir dev-env/CC/nanox + + wget https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q1-update/+download/gcc-arm-none-eabi-5_3-2016q1-20160330-linux.tar.bz2 + tar xf gcc-arm-none-eabi-5_3-2016q1-20160330-linux.tar.bz2 + rm gcc-arm-none-eabi-5_3-2016q1-20160330-linux.tar.bz2 + cp -r gcc-arm-none-eabi-5_3-2016q1 dev-env/CC/nanox/gcc-arm-none-eabi-5_3-2016q1 + mv gcc-arm-none-eabi-5_3-2016q1 dev-env/CC/others/gcc-arm-none-eabi-5_3-2016q1 + + wget http://releases.llvm.org/4.0.0/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-16.10.tar.xz -O clang+llvm.tar.xz + tar xf clang+llvm.tar.xz + rm clang+llvm.tar.xz + mv clang+llvm* dev-env/CC/others/clang-arm-fropi + + wget http://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz -O clang+llvm.tar.xz + tar xf clang+llvm.tar.xz + rm clang+llvm.tar.xz + mv clang+llvm* dev-env/CC/nanox/clang-arm-fropi + + wget https://github.com/LedgerHQ/blue-secure-sdk/archive/blue-r21.1.tar.gz -O blue-secure-sdk.tar.gz + tar xf blue-secure-sdk.tar.gz + rm blue-secure-sdk.tar.gz + mv blue-secure-sdk* dev-env/SDK/blue-secure-sdk + + wget https://github.com/LedgerHQ/nanos-secure-sdk/archive/nanos-160.tar.gz -O nanos-secure-sdk.tar.gz + tar xf nanos-secure-sdk.tar.gz + rm nanos-secure-sdk.tar.gz + mv nanos-secure-sdk* dev-env/SDK/nanos-secure-sdk + + python3 -m venv dev-env/ledger_py3 + source dev-env/ledger_py3/bin/activate + pip install wheel + pip install ledgerblue +fi + + +source dev-env/ledger_py3/bin/activate + +if [[ $1 == "blue" ]]; then + export BOLOS_SDK=$(pwd)/dev-env/SDK/blue-secure-sdk + export BOLOS_ENV=$(pwd)/dev-env/CC/others +elif [[ $1 == "s" ]]; then + export BOLOS_SDK=$(pwd)/dev-env/SDK/nanos-secure-sdk + export BOLOS_ENV=$(pwd)/dev-env/CC/others +elif [[ $1 == "x" ]]; then + export BOLOS_SDK=$(pwd)/dev-env/SDK/nanox-secure-sdk + export BOLOS_ENV=$(pwd)/dev-env/CC/nanox +fi diff --git a/src/ardor.c b/src/ardor.c deleted file mode 100644 index f935ffd..0000000 --- a/src/ardor.c +++ /dev/null @@ -1,236 +0,0 @@ -/******************************************************************************* -* (c) 2019 Haim Bender -* -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -********************************************************************************/ - -#include -#include - -#include -#include -#include - -#include "curve25519_i64.h" -#include "returnValues.h" -#include "config.h" - -#include "ardor.h" - - - - -//the global state -states_t state; - - -//self explanatory -//output must point to buffer of 32 bytes in size -void sha256TwoBuffers(const uint8_t * const bufferTohash1, const uint16_t sizeOfBuffer1, const uint8_t * const bufferTohash2, const uint16_t sizeOfBuffer2, uint8_t * const output) { - cx_sha256_t shaContext; - - os_memset(output, 0, 32); - cx_sha256_init(&shaContext); //return value has no info - - cx_hash(&shaContext.header, 0, bufferTohash1, sizeOfBuffer1, output, 32); - - if (0 != bufferTohash2) - cx_hash(&shaContext.header, 0, bufferTohash2, sizeOfBuffer2, output, 32); - - cx_hash(&shaContext.header, CX_LAST, 0, 0, output, 32); -} - -//self explanatory -//output must point to buffer of 32 bytes in size -void sha256Buffer(const uint8_t * const bufferTohash, const uint16_t sizeOfBuffer, uint8_t * const output) { - sha256TwoBuffers(bufferTohash, sizeOfBuffer, 0, 0, output); -} - -//This is the EC-KCDSA siging implementation -//@param in: keySeedBfr should point to a 32 byte keyseed (privateKey ^ -1) - note this function can edit keySeedBfr in the process -//@parma in: msgSha256 should point to a 32 byte sha256 of the message we are signing -//@param out: sig should point to 64 bytes allocated to hold the signiture of the message -void signMsg(uint8_t * const keySeedBfr, const uint8_t * const msgSha256, uint8_t * const sig) { - - uint8_t publicKeyX[32], privateKey[32]; os_memset(publicKeyX, 0, sizeof(publicKeyX)); os_memset(privateKey, 0, sizeof(privateKey)); - - keygen25519(publicKeyX, privateKey, keySeedBfr); - - uint8_t x[32]; os_memset(x, 0, sizeof(x)); - - sha256TwoBuffers(msgSha256, 32, privateKey, sizeof(privateKey), x); - - uint8_t Y[32]; os_memset(Y, 0, sizeof(Y)); - - keygen25519(Y, 0, x); - - uint8_t h[32]; os_memset(h, 0, sizeof(h)); - - sha256TwoBuffers(msgSha256, 32, Y, sizeof(Y), h); - - os_memmove(sig + 32, h, 32); - - sign25519(sig, h, x, privateKey); -} - - -//from curveConversion.C -void morph25519_e2m(uint8_t *montgomery, const uint8_t *y); - - -//this function derives an ardor keeyseed (privatekey ^ -1), public key, ed255119 public key and chaincode -//For more info on how this derivation works, please read the readme -//@param in: derivationPath - a BIP42 derivation path, must be at least of length MIN_DERIVATION_PATH_LENGTH -//@param in: derivationPathLengthInUints32 - kinda what it says it is -//@param optional out: keySeedBfrOut - 32 byte EC-KCDSA keyseed for the derivation path -//@param optional out: publicKeyCurveXout - 32 byte EC-KCDSA public key for the derivation path -//@param optional out: publicKeyEd25519YLEWithXParityOut - 32 byte ED255119 public key for the derivation path (used for debuging), with the MSB as X's parity -//@param optional out: chainCodeOut - the 32 byte ED255119 derivation chaincode, used for external master public key derivation -//@param out: exceptionOut - iff the return code is R_EXCEPTION => exceptionOut will be filled with the Nano exception code -//@returns: regular return values -uint8_t ardorKeys(const uint8_t * const derivationPath, const uint8_t derivationPathLengthInUints32, - uint8_t * const keySeedBfrOut, uint8_t * const publicKeyCurveXout, uint8_t * const publicKeyEd25519YLEWithXParityOut, uint8_t * const chainCodeOut, uint16_t * const exceptionOut) { - - uint8_t publicKeyYLE[32]; os_memset(publicKeyYLE, 0, sizeof(publicKeyYLE)); //declaring here although used later, so it can be acessable to the finally statement - uint8_t KLKR[64]; os_memset(KLKR, 0, sizeof(KLKR)); - struct cx_ecfp_256_private_key_s privateKey; //Don't need to init, since the ->d is copied into from some other palce, this key is 32 bytes in size - - uint32_t bipPrefix[] = PATH_PREFIX; //defined in Makefile - - if ((MIN_DERIVATION_LENGTH > derivationPathLengthInUints32) || (MAX_DERIVATION_LENGTH < derivationPathLengthInUints32)) - return R_WRONG_SIZE_ERR; - - //os_perso_derive_node_bip32 doesn't accept derivation paths located on the input buffer, so we make a local stack copy - uint32_t copiedDerivationPath[MAX_DERIVATION_LENGTH]; os_memset(copiedDerivationPath, 0, sizeof(copiedDerivationPath)); - os_memmove(copiedDerivationPath, derivationPath, derivationPathLengthInUints32 * sizeof(uint32_t)); - - for (uint8_t i = 0; i < sizeof(bipPrefix) / sizeof(bipPrefix[0]); i++) { - if (copiedDerivationPath[i] != bipPrefix[i]) - return R_WRONG_DERIVATION_PATH_HEADER; - } - - BEGIN_TRY { - TRY { - os_perso_derive_node_bip32(CX_CURVE_Ed25519, copiedDerivationPath, derivationPathLengthInUints32, KLKR, chainCodeOut); - - // weird custom initilization, code copied from Cardano's EdDSA implementaion - privateKey.curve = CX_CURVE_Ed25519; - privateKey.d_len = 64; //don't know why the length is 64 instead of 32, it just works - os_memmove(privateKey.d, KLKR, 32); //Copy just the KL part - - //KL is the keeyseed, KR is used for key derivation - if (0 != keySeedBfrOut) { - //os_memmove(keySeedBfrOut, KLKR, 64); used for testing - DO NOT COMMIT THIS LINE! DO NOT COMMIT THIS LINE!, most functions expect a 32 private key and they will get stack overwtite - os_memmove(keySeedBfrOut, KLKR, 32); - } - - if ((0 != publicKeyCurveXout) || (0 != publicKeyEd25519YLEWithXParityOut)) { - - cx_ecfp_public_key_t publicKey; - cx_ecfp_init_public_key(CX_CURVE_Ed25519, 0, 0, &publicKey); - - //This should return A = KL * B - B is the generator point in ED25519 - //So publicKey.W = 04 Ax Ay in BE - cx_eddsa_get_public_key( - &privateKey, - CX_SHA512, - &publicKey, - NULL, 0, NULL, 0); - - // copy public key from big endian to little endian - for (uint8_t i = 0; i < sizeof(publicKeyYLE); i++) - publicKeyYLE[i] = publicKey.W[64 - i]; - - if (0 != publicKeyCurveXout) - morph25519_e2m(publicKeyCurveXout, publicKeyYLE); - - //We encode the pairty of X into the MSB of Y, since it's never used because of the feild size - //This allows us to compress X,Y into 32 bytes - if ((publicKey.W[32] & 1) != 0) - publicKeyYLE[31] |= 0x80; - - if (0 != publicKeyEd25519YLEWithXParityOut) - os_memmove(publicKeyEd25519YLEWithXParityOut, publicKeyYLE, 32); - } - } - CATCH_OTHER(exception) { - *exceptionOut = exception; - return R_KEY_DERIVATION_EX; - } - FINALLY { - os_memset(privateKey.d, 0, privateKey.d_len); - os_memset(KLKR, 0, sizeof(KLKR)); - os_memset(publicKeyYLE, 0, sizeof(publicKeyYLE)); - } - } - END_TRY; - - return R_SUCCESS; -} - -//Creates a shared AES encryption key one the matches the key related to the derivation path, the target public key and the nonce -//@param derivationPath - the derivation path -//@param derivationPathLengthInUints32 - kinda clear what this is -//@param targetPublicKey - the 32 byte public key -uint8_t getSharedEncryptionKey(const uint8_t * const derivationPath, const uint8_t derivationPathLengthInUints32, const uint8_t* const targetPublicKey, - const uint8_t * const nonce, uint16_t * const exceptionOut, uint8_t * const aesKeyOut) { - - uint8_t keySeed[32]; os_memset(keySeed, 0, sizeof(keySeed)); - - uint8_t ret = ardorKeys(derivationPath, derivationPathLengthInUints32, keySeed, 0, 0, 0, exceptionOut); - - if (R_SUCCESS != ret) - return ret; - - uint8_t sharedKey[32]; os_memset(sharedKey, 0, sizeof(sharedKey)); - - - curve25519(sharedKey, keySeed, targetPublicKey); //should use only the first 32 bytes of keyseed - - for (uint8_t i = 0; i < sizeof(sharedKey); i++) - sharedKey[i] ^= nonce[i]; - - sha256Buffer(sharedKey, sizeof(sharedKey), aesKeyOut); - - return R_SUCCESS; -} - -//param: publicKey should point to a 32 byte public key buffer -//returns: a 64bit public key id, used later with reedsolomon to create Ardor/NXT addresses -uint64_t publicKeyToId(const uint8_t * const publicKey) { - - uint8_t tempSha[32]; - sha256Buffer(publicKey, 32, tempSha); - - return ((((uint64_t) tempSha[7]) << 56) | - (((uint64_t) tempSha[6]) << 48) | - (((uint64_t) tempSha[5]) << 40) | - (((uint64_t) tempSha[4]) << 32) | - (((uint64_t) tempSha[3]) << 24) | - (((uint64_t) tempSha[2]) << 16) | - (((uint64_t) tempSha[1]) << 8) | - (((uint64_t) tempSha[0] ))); -} - - -//app_stack_canary is defined by the link script to be at the start of the user data or end of the stack, something like that -//so if there is a stack overflow then it will be overwriten, this is how check_canary() works. -//make sure HAVE_BOLOS_APP_STACK_CANARY is defined in the makefile, so that the OS code will init it and check against it every io_exchange call -//if the canary is not the same, and if not, it will throw - -extern unsigned int app_stack_canary; - -bool check_canary() { - return 0xDEAD0031 == app_stack_canary; -} diff --git a/src/authAndSignTxn.c b/src/authAndSignTxn.c index d4de509..2b10f94 100644 --- a/src/authAndSignTxn.c +++ b/src/authAndSignTxn.c @@ -28,86 +28,70 @@ #include "glyphs.h" #include "returnValues.h" #include "config.h" -#include "ardor.h" +#include "burst.h" -#define P1_INIT 1 -#define P1_CONTINUE 2 -#define P1_SIGN 3 +#define P1_SIGN_INIT 1 +#define P1_SIGN_CONTINUE 2 +#define P1_SIGN_FINISH 3 +#define PARSE_MAIN_TXN 1 +#define PARSE_APPENDAGES 2 +#define PARSE_REFERENCED_TXN 3 +#define PARSE_ORDER_PLACEMENT 5 +#define PARSE_IGONORE 6 // This is the code that parses the txn for signing, it parses streamed txn bytes into the state object while hashing the bytes to be signed later, // displays a dialog of screens which contain the parsed txn bytes from the state, -// It solves 2 no trivial problems -// 1 - Allowing txn bytes to be parsed from a stream of bytes (this is very hard, since we don't have a lot memory, so we need to parse bytes and forget about them) -// 2 - txn's very in length depending on type, so the same bytes are sometimes not parsed into the same place, so it has to be dynamic about parsing -// -// -// The way these problems are solved is by the following flow: -// -// The function stack is initiated with an index reference to parseMainTxnData -// // // authAndSignTxnHandlerHelper is called with some of the txn bytes // => addToReadBuffer is called adds these bytes to the read buffer -// => parseFromStack is called, which calls the first parse function on the stack which is parseMainTxnData -// => parseMainTxnData trys to pull 142 bytes from the buffer -// if there is are 142 bytes there: -// parseMainTxnData parses the main txn bytes and then adds more functions to parse stack depending on the appendages +// => parseMainTxnData tries to pull 176 bytes from the buffer and after that the supported attachments +// if there is are 176 bytes available: +// parseMainTxnData parses the main txn bytes and tries to read possible attachments // else -// R_SEND_MORE_BYTES is trickled down by the function to be sent to back to the client +// R_SEND_MORE_BYTES returned back to the client // -// and so on the process goes until the stack of functions is empty and there are no more bytes in the read buffer -// if the parsing goes well without errors => setScreenTexts(); is called which sets up the labels and first screen of the autherization dialog -// => showScreen(); make the first screen apeare setting and sets ui_auth_button() to be the button callback for the dialog => -// pressing on the right does state.txnAuth.dialogScreenIndex++; if it reaches the end number then the txn is autherized for signing and state.txnAuth.txnPassedAutherization is set to true -// pressing left does state.txnAuth.dialogScreenIndex--; and if it gets to a negative number it will be interpretate as a txn rejection => initTxnAuthState() -// will be called and R_REJECT will be returned to client +// If the parsing goes well without errors screen texts are already configured and shown to the user on the dongle. +// This will block execution and wait for the user to either authorize or reject the request. // API: // // // The mode is encoded in the first 2 bits of the p1 parameter and the size of the txn should be ((p1 & 0b11111100) << 6) + p2 -// you only need to pass the size when calling P1_INIT +// you only need to pass the size when calling P1_SIGN_INIT // -// P1: P1_INIT: +// P1: P1_SIGN_INIT: initialize the signing command, with the txn to sign // dataBuffer: txn bytes //you can send all of your bytes here if you want // returns: 1 byte status // -// P1: P1_CONTINUE: more txn bytes +// P1: P1_SIGN_CONTINUE: more txn bytes // returns: 1 byte status // -// P1: P1_SIGN: -// dataBuffer: derivaiton path (uint32) * some length +// P1: P1_SIGN_FINISH: closes the signing command (no data should be sent) +// P2: account index // returns: 1 bytes status | 64 byte signiture -//This function cleans the txnAuth part of the state, its important to call it before starting to load a txn -//also whenever there is an error you should call it so that no one can exploit an error state for some sort of attack, -//the cleaner the state is, the better, allways clean when you can +// This function cleans the txnAuth part of the state. It is important to call it before starting to load a txn +// also whenever there is an error you should call it so that no one can exploit an error state for some sort of attack. +// The cleaner the state is, the better, allways clean when you can. void initTxnAuthState() { state.txnAuth.txnSizeBytes = 0; state.txnAuth.numBytesRead = 0; - os_memset(state.txnAuth.functionStack, 0, sizeof(state.txnAuth.functionStack)); - state.txnAuth.functionStack[0] = 1; //Add the first parse function on the stack - state.txnAuth.functionStack[1] = 2; //The appendages parse function - state.txnAuth.numFunctionsOnStack = 2; - state.txnAuth.txnPassedAutherization = false; state.txnAuth.isClean = true; cx_sha256_init(&state.txnAuth.hashstate); - os_memset(state.txnAuth.readBuffer, 0, sizeof(state.txnAuth.readBuffer)); + explicit_bzero(state.txnAuth.readBuffer, sizeof(state.txnAuth.readBuffer)); state.txnAuth.readBufferReadOffset = 0; state.txnAuth.readBufferEndPos = 0; - state.txnAuth.chainId = 0; state.txnAuth.txnTypeAndSubType = 0; - state.txnAuth.txnTypeIndex = 0; state.txnAuth.recipientId = 0; state.txnAuth.amount = 0; @@ -117,45 +101,18 @@ void initTxnAuthState() { state.txnAuth.attachmentTempInt64Num2 = 0; state.txnAuth.attachmentTempInt64Num3 = 0; - os_memset(state.txnAuth.feeText, 0, sizeof(state.txnAuth.feeText)); - os_memset(state.txnAuth.chainAndTxnTypeText, 0, sizeof(state.txnAuth.chainAndTxnTypeText)); - os_memset(state.txnAuth.optionalWindow1Text, 0, sizeof(state.txnAuth.optionalWindow1Text)); - os_memset(state.txnAuth.optionalWindow2Title, 0, sizeof(state.txnAuth.optionalWindow2Title)); - os_memset(state.txnAuth.optionalWindow2Text, 0, sizeof(state.txnAuth.optionalWindow2Text)); - os_memset(state.txnAuth.appendagesText, 0, sizeof(state.txnAuth.appendagesText)); - - state.txnAuth.uiFlowBitfeild = 0; -} - - -//Does what it says -txnType * txnTypeAtIndex(const uint8_t index) { - //Because static memory is weird and might be reclocated in ledger we have to use the PIC macro in order to access it - return (txnType*)PIC(&TXN_TYPES[index]); -} - -//does what it says -char * txnTypeNameAtIndex(const uint8_t index) { - //Because static memory is weird and might be reclocated in ledger we have to use the PIC macro in order to access it - return (char*)PIC(((txnType*)PIC(&TXN_TYPES[index]))->name); -} - -//does what is says -char * chainName(const uint8_t chainId) { - //Because static memory is weird and might be reclocated in ledger we have to use the PIC macro in order to access it - return (char*)PIC(((chainType*)PIC(&CHAINS[chainId - 1]))->name); -} - -//the amount of digits on the right of the decimal dot for each chain -uint8_t chainNumDecimalsBeforePoint(const uint8_t chainId) { - //Because static memory is weird and might be reclocated in ledger we have to use the PIC macro in order to access it - return ((chainType*)PIC(&CHAINS[chainId - 1]))->numDecimalsBeforePoint; + explicit_bzero(state.txnAuth.feeText, sizeof(state.txnAuth.feeText)); + explicit_bzero(state.txnAuth.txnTypeText, sizeof(state.txnAuth.txnTypeText)); + explicit_bzero(state.txnAuth.optionalWindow1Text, sizeof(state.txnAuth.optionalWindow1Text)); + explicit_bzero(state.txnAuth.optionalWindow2Title, sizeof(state.txnAuth.optionalWindow2Title)); + explicit_bzero(state.txnAuth.optionalWindow2Text, sizeof(state.txnAuth.optionalWindow2Text)); + explicit_bzero(state.txnAuth.appendagesText, sizeof(state.txnAuth.appendagesText)); } //this function formats amounts into string and most importantly add the dot where it's supposed to be //the way this is works is that amounts ints and then the dot is added after chainNumDecimalsBeforePoint() digits from right to left -//for example, if the amount is 4200000000 and we are in the Ardor chain in which chainNumDecimalsBeforePoint() is 8 then the formated amount will be "42" +//for example, if the amount is 4200000000 and we have 8 decimals, then the formated amount will be "42" //for 4210100000 it will be 42.101 //@param outputString - does what it says @@ -171,7 +128,7 @@ uint8_t formatAmount(char * const outputString, const uint16_t maxOutputLength, uint8_t numberIndex = 0; - while (42) { + for (;;) { uint8_t modulo = numberToFormat % 10; numberToFormat -= modulo; @@ -218,7 +175,7 @@ uint8_t formatAmount(char * const outputString, const uint16_t maxOutputLength, void reedSolomonEncode(const uint64_t inp, const char * output); //Accept click callback -unsigned int txn_autherized(const bagl_element_t *e) { +unsigned int txn_authorized(const bagl_element_t *e) { UNUSED(e); state.txnAuth.txnPassedAutherization = true; @@ -249,24 +206,24 @@ unsigned int txn_canceled(const bagl_element_t *e) { } //Defenition of the UI for the handler -UX_STEP_NOCB(aasFlowPage1, +UX_STEP_NOCB(aasFlowAuthorize, pnn, { &C_icon_eye, "Authorize", "transaction", }); -UX_STEP_NOCB(aasFlowPage2, +UX_STEP_NOCB(aasFlowTxType, bnnn_paging, { - .title = "Chain&TxnType", - .text = state.txnAuth.chainAndTxnTypeText, + .title = "Transaction Type", + .text = state.txnAuth.txnTypeText, }); UX_STEP_NOCB(aasFlowOptional1, bnnn_paging, { - .title = "Amount", + .title = state.txnAuth.optionalWindow1Title, .text = state.txnAuth.optionalWindow1Text, }); UX_STEP_NOCB(aasFlowOptional2, @@ -278,24 +235,23 @@ UX_STEP_NOCB(aasFlowOptional2, UX_STEP_NOCB(aasFlowAppendages, bnnn_paging, { - .title = "Appendages", + .title = state.txnAuth.appendagesTitle, .text = state.txnAuth.appendagesText, }); -UX_STEP_NOCB(aasFlowPage3, +UX_STEP_NOCB(aasFlowFees, bnnn_paging, { - .title = "Fees", + .title = "Fees in BURST", .text = state.txnAuth.feeText, }); -UX_STEP_VALID(aasFlowPage4, - pbb, - txn_autherized(NULL), +UX_STEP_VALID(aasFlowAccept, + pb, + txn_authorized(NULL), { &C_icon_validate_14, "Accept", - "and send", }); -UX_STEP_VALID(aasFlowPage5, +UX_STEP_VALID(aasFlowReject, pb, txn_canceled(NULL), { @@ -303,131 +259,59 @@ UX_STEP_VALID(aasFlowPage5, "Reject", }); -UX_FLOW(ux_flow_00, - &aasFlowPage1, - &aasFlowPage2, - &aasFlowPage3, - &aasFlowPage4, - &aasFlowPage5 +UX_FLOW(ux_flow_minimal, + &aasFlowAuthorize, + &aasFlowTxType, + &aasFlowFees, + &aasFlowAccept, + &aasFlowReject ); -UX_FLOW(ux_flow_01, - &aasFlowPage1, - &aasFlowPage2, +UX_FLOW(ux_flow_appendages, + &aasFlowAuthorize, + &aasFlowTxType, &aasFlowAppendages, - &aasFlowPage3, - &aasFlowPage4, - &aasFlowPage5 + &aasFlowFees, + &aasFlowAccept, + &aasFlowReject ); -UX_FLOW(ux_flow_10, - &aasFlowPage1, - &aasFlowPage2, +UX_FLOW(ux_flow_1optional, + &aasFlowAuthorize, + &aasFlowTxType, &aasFlowOptional1, - &aasFlowOptional2, - &aasFlowPage3, - &aasFlowPage4, - &aasFlowPage5 + &aasFlowFees, + &aasFlowAccept, + &aasFlowReject ); -UX_FLOW(ux_flow_11, - &aasFlowPage1, - &aasFlowPage2, +UX_FLOW(ux_flow_optionals, + &aasFlowAuthorize, + &aasFlowTxType, &aasFlowOptional1, &aasFlowOptional2, + &aasFlowFees, + &aasFlowAccept, + &aasFlowReject +); + +UX_FLOW(ux_flow_optionals_and_appendages, + &aasFlowAuthorize, + &aasFlowTxType, + &aasFlowOptional1, &aasFlowAppendages, - &aasFlowPage3, - &aasFlowPage4, - &aasFlowPage5 + &aasFlowOptional2, + &aasFlowFees, + &aasFlowAccept, + &aasFlowReject ); //Just switches between based of the uiFlowBitfeild static void showScreen() { - if(0 == G_ux.stack_count) ux_stack_push(); - switch (state.txnAuth.uiFlowBitfeild) { - - case 0x00: - ux_flow_init(0, ux_flow_00, NULL); - break; - case 0x01: - ux_flow_init(0, ux_flow_01, NULL); - break; - case 0x02: - ux_flow_init(0, ux_flow_10, NULL); - break; - case 0x03: - ux_flow_init(0, ux_flow_11, NULL); - break; - } -} - -//This function formats trxn data into text memebers of the state which the UI flow will read from -//Returns: Success iff everything is good, otherwise probably some kind of formating error -uint8_t setScreenTexts() { - - uint8_t ret = 0; - - if (LEN_TXN_TYPES > state.txnAuth.txnTypeIndex) { - - state.txnAuth.uiFlowBitfeild |= 2; //turn on the second bit - - switch (state.txnAuth.txnTypeAndSubType) { - - //note: you have to write the type and subtype in reverse, because of little endian buffer representation an big endian C code representation - - case 0x0000: //OrdinaryPayment - case 0x00fe: //FxtPayment - - if (0 == formatAmount(state.txnAuth.optionalWindow1Text, sizeof(state.txnAuth.optionalWindow1Text), state.txnAuth.amount, chainNumDecimalsBeforePoint(state.txnAuth.chainId))) - return R_FORMAT_AMOUNT_ERR; - - snprintf(state.txnAuth.optionalWindow2Title, sizeof(state.txnAuth.optionalWindow2Title), "Recipient"); - snprintf(state.txnAuth.optionalWindow2Text, sizeof(state.txnAuth.optionalWindow2Text), APP_PREFIX); - reedSolomonEncode(state.txnAuth.recipientId, state.txnAuth.optionalWindow2Text + strlen(state.txnAuth.optionalWindow2Text)); - - break; - - case 0x00fc: //FxtCoinExchangeOrderIssue - case 0x000b: //CoinExchangeOrderIssue - - ret = formatAmount(state.txnAuth.optionalWindow1Text, sizeof(state.txnAuth.optionalWindow1Text), state.txnAuth.attachmentTempInt64Num1, chainNumDecimalsBeforePoint(state.txnAuth.attachmentTempInt32Num2)); - - if (0 == ret) - return R_FORMAT_AMOUNT_ERR; - - //note: the existence of chainName(state.txnAuth.attachmentTempInt32Num2) was already checked in the parsing function - snprintf(state.txnAuth.optionalWindow1Text + ret - 1, sizeof(state.txnAuth.optionalWindow1Text) - ret - 1, " %s", chainName(state.txnAuth.attachmentTempInt32Num2)); - - //note: the existence of chainName(state.txnAuth.attachmentTempInt32Num2) was already checked in the parsing function - snprintf(state.txnAuth.optionalWindow2Title, sizeof(state.txnAuth.optionalWindow2Title), "Price per %s", chainName(state.txnAuth.attachmentTempInt32Num2)); - ret = formatAmount(state.txnAuth.optionalWindow2Text, sizeof(state.txnAuth.optionalWindow2Text), state.txnAuth.attachmentTempInt64Num2, chainNumDecimalsBeforePoint(state.txnAuth.attachmentTempInt32Num1)); - - if (0 == ret) - return R_FORMAT_AMOUNT_ERR; - - //note: the existence of chainName(state.txnAuth.attachmentTempInt32Num1) was already checked in the parsing function - snprintf(state.txnAuth.optionalWindow2Text + ret - 1, sizeof(state.txnAuth.optionalWindow2Text) - ret - 1, " %s", chainName(state.txnAuth.attachmentTempInt32Num1)); - - break; - default: - state.txnAuth.uiFlowBitfeild &= (0xff - 2); //since we don't fall under a txn type that needs 2 extra windows, turn off the second bit - } - } - - return R_SUCCESS; -} - -//Does what is says -uint8_t addToFunctionStack(const uint8_t functionNum) { - if (sizeof(state.txnAuth.functionStack) == state.txnAuth.numFunctionsOnStack) - return R_FUNCTION_STACK_FULL; - - state.txnAuth.functionStack[state.txnAuth.numFunctionsOnStack++] = functionNum; - - return R_SUCCESS; + ux_flow_init(0, state.txnAuth.ux_flow, NULL); } //Takes bytes away from the buffer, returns 0 if there aren't enough bytes @@ -443,51 +327,30 @@ uint8_t * readFromBuffer(const uint8_t size) { return ret; } -//This is the main parse function, it parses the main txn body and adds more function to the parse stack if needed -uint8_t parseMainTxnData() { - - uint8_t * ptr = readFromBuffer(145); +// Adds the token ID to Window 1 +uint8_t addTokenID() { + snprintf(state.txnAuth.optionalWindow1Title, sizeof(state.txnAuth.optionalWindow1Title), "%s", "Token ID"); + return formatAmount(state.txnAuth.optionalWindow1Text, sizeof(state.txnAuth.optionalWindow1Text), state.txnAuth.attachmentTempInt64Num1, 0); +} + +// Adds to Window 2 the recipient +void addRecipientText() { + snprintf(state.txnAuth.optionalWindow2Title, sizeof(state.txnAuth.optionalWindow2Title), "Recipient"); + snprintf(state.txnAuth.optionalWindow2Text, sizeof(state.txnAuth.optionalWindow2Text), APP_PREFIX); + reedSolomonEncode(state.txnAuth.recipientId, state.txnAuth.optionalWindow2Text + strlen(state.txnAuth.optionalWindow2Text)); +} +// This is the parse function, it parses the entire txn body and configure ux_flow variables +uint8_t parseMainTxnData() { + // Parse the byte array as construted by brs.Transaction.getBytes() + uint8_t pos; + uint8_t *ptr = readFromBuffer(176); // minimal transaction length if (0 == ptr) return R_SEND_MORE_BYTES; - os_memmove(&(state.txnAuth.chainId), ptr, sizeof(state.txnAuth.chainId)); - - ptr += sizeof(state.txnAuth.chainId); - - if ((0 == state.txnAuth.chainId) || (NUM_CHAINS < state.txnAuth.chainId)) //note: we do +1 here because ardor start with index 1 - return R_BAD_CHAIN_ID_ERR; - - os_memmove(&(state.txnAuth.txnTypeAndSubType), ptr, sizeof(state.txnAuth.txnTypeAndSubType)); - ptr += sizeof(state.txnAuth.txnTypeAndSubType); - txnType * currentTxnType = 0; - - for (state.txnAuth.txnTypeIndex = 0; state.txnAuth.txnTypeIndex < LEN_TXN_TYPES; state.txnAuth.txnTypeIndex++) { - - currentTxnType = txnTypeAtIndex(state.txnAuth.txnTypeIndex); - - if (currentTxnType->id == state.txnAuth.txnTypeAndSubType) - break; - } - - if (LEN_TXN_TYPES > state.txnAuth.txnTypeIndex) //goto check if the index in range before accessing the array - if (0 != currentTxnType->attachmentParsingFunctionNumber) - addToFunctionStack(currentTxnType->attachmentParsingFunctionNumber); - - if (LEN_TXN_TYPES > state.txnAuth.txnTypeIndex) { - snprintf(state.txnAuth.chainAndTxnTypeText, sizeof(state.txnAuth.chainAndTxnTypeText), "%s %s", chainName(state.txnAuth.chainId), txnTypeNameAtIndex(state.txnAuth.txnTypeIndex)); - } else { - snprintf(state.txnAuth.chainAndTxnTypeText, sizeof(state.txnAuth.chainAndTxnTypeText), "%s UnknownTxnType", chainName(state.txnAuth.chainId)); - } - - if (SUPPORTED_TXN_VERSION != *((uint8_t*)ptr)) - return R_WRONG_VERSION_ERR; - - ptr += sizeof(uint8_t); - ptr += 4; // Skip the timestamp ptr += 2; // Skip the deadline ptr += 32; // Skip the sender publickey @@ -500,120 +363,148 @@ uint8_t parseMainTxnData() { uint64_t fee = 0; os_memmove(&fee, ptr, sizeof(fee)); + ptr += sizeof(fee); - uint8_t ret = formatAmount(state.txnAuth.feeText, sizeof(state.txnAuth.feeText), fee, chainNumDecimalsBeforePoint(state.txnAuth.chainId)); - + uint8_t ret = formatAmount(state.txnAuth.feeText, sizeof(state.txnAuth.feeText), fee, 8); if (0 == ret) return R_FORMAT_FEE_ERR; - snprintf(state.txnAuth.feeText + ret - 1, sizeof(state.txnAuth.feeText) - ret - 1, " %s", chainName(state.txnAuth.chainId)); - - ptr += sizeof(uint64_t); - + ptr += 32; //Skip the referencedTransactionFullHash TODO: check ptr += 64; //Skip the sig + + ptr += 4; //Skip the flags ptr += 4; //Skip the block height ptr += 8; //Skip the block Id - addToFunctionStack(6); - - return R_SUCCESS; -} + state.txnAuth.ux_flow = ux_flow_minimal; // minimal ux_flow + + // Configure windows and read appendages, see brs.Attachment.java + char *txTypeText = NULL; + switch (state.txnAuth.txnTypeAndSubType) { + case 0x1000: + txTypeText = "BURST Transfer"; + state.txnAuth.ux_flow = ux_flow_optionals; + + // Window 1 is amount + snprintf(state.txnAuth.optionalWindow1Title, sizeof(state.txnAuth.optionalWindow1Title), "%s", "BURST Amount"); + pos = formatAmount(state.txnAuth.optionalWindow1Text, sizeof(state.txnAuth.optionalWindow1Text), state.txnAuth.amount, 8); + if (0 == pos) + return R_FORMAT_AMOUNT_ERR; + // Window 2 is the recipient + addRecipientText(); + + break; + case 0x1102: + txTypeText = "Token Transfer"; + state.txnAuth.ux_flow = ux_flow_optionals; + + ptr = readFromBuffer(1 + 8*2); // version plus 2 longs + if (ptr == 0) + return R_SEND_MORE_BYTES; + ptr += 1; //version + os_memmove(&(state.txnAuth.attachmentTempInt64Num1), ptr, sizeof(state.txnAuth.attachmentTempInt64Num1)); // assetID + ptr += sizeof(state.txnAuth.attachmentTempInt64Num1); + os_memmove(&(state.txnAuth.attachmentTempInt64Num2), ptr, sizeof(state.txnAuth.attachmentTempInt64Num2)); // quantity + ptr += sizeof(state.txnAuth.attachmentTempInt64Num2); + + if(state.txnAuth.attachmentTempInt64Num1 == TRT_TOKEN){ + // TRT token + txTypeText = "TRT Transfer"; + snprintf(state.txnAuth.optionalWindow1Title, sizeof(state.txnAuth.optionalWindow1Title), "%s", "TRT Amount"); + pos = formatAmount(state.txnAuth.optionalWindow1Text, sizeof(state.txnAuth.optionalWindow1Text), state.txnAuth.attachmentTempInt64Num2, 4); + if (0 == pos) + return R_FORMAT_AMOUNT_ERR; + } + else { + // Window 1 is token ID + if(addTokenID() == 0) + return R_FORMAT_AMOUNT_ERR; + // Amount not shown as there is no information about the number of decimal places + } + // Window 2 is the recipient + addRecipientText(); -//Parses a txn reference, by just skiping over the bytes :) -uint8_t parseReferencedTxn() { + break; - if (0 == readFromBuffer(sizeof(uint32_t) + 32)) - return R_SEND_MORE_BYTES; + case 0x1202: + case 0x1302: + // Place token offer + ptr = readFromBuffer(1 + 8*3); // version plus 3 longs + if (ptr == 0) + return R_SEND_MORE_BYTES; + ptr += 1; //version + + os_memmove(&(state.txnAuth.attachmentTempInt64Num1), ptr, sizeof(state.txnAuth.attachmentTempInt64Num1)); // assetID + ptr += sizeof(state.txnAuth.attachmentTempInt64Num1); + os_memmove(&(state.txnAuth.attachmentTempInt64Num2), ptr, sizeof(state.txnAuth.attachmentTempInt64Num2)); // quantity + ptr += sizeof(state.txnAuth.attachmentTempInt64Num2); + os_memmove(&(state.txnAuth.attachmentTempInt64Num3), ptr, sizeof(state.txnAuth.attachmentTempInt64Num3)); // price + ptr += sizeof(state.txnAuth.attachmentTempInt64Num3); + + if(state.txnAuth.attachmentTempInt64Num1 == TRT_TOKEN){ + txTypeText = "Place TRT Offer"; + state.txnAuth.ux_flow = ux_flow_optionals; + + snprintf(state.txnAuth.optionalWindow1Title, sizeof(state.txnAuth.optionalWindow1Title), "%s", "TRT Amount"); + pos = formatAmount(state.txnAuth.optionalWindow1Text, sizeof(state.txnAuth.optionalWindow1Text), state.txnAuth.attachmentTempInt64Num2, 4); + if (0 == pos) + return R_FORMAT_AMOUNT_ERR; + + snprintf(state.txnAuth.optionalWindow2Title, sizeof(state.txnAuth.optionalWindow2Title), "%s", "Price in BURST"); + pos = formatAmount(state.txnAuth.optionalWindow2Text, sizeof(state.txnAuth.optionalWindow2Text), state.txnAuth.attachmentTempInt64Num3, 4); + if (0 == pos) + return R_FORMAT_AMOUNT_ERR; + + } + else { + txTypeText = "Place Offer"; + state.txnAuth.ux_flow = ux_flow_1optional; - return R_SUCCESS; -} + // Window 1 is token ID + if(addTokenID() == 0) + return R_FORMAT_AMOUNT_ERR; + } -//Does what it says, it's added to function stack on init -uint8_t parseAppendagesFlags() { - - uint8_t * ptr = readFromBuffer(sizeof(uint32_t)); + break; - if (0 == ptr) - return R_SEND_MORE_BYTES; - - uint32_t appendages = 0; - - os_memmove(&appendages, ptr, sizeof(appendages)); + case 0x1402: + case 0x1502: + txTypeText = "Cancel Offer"; + state.txnAuth.ux_flow = ux_flow_1optional; - if (0 != appendages) { - state.txnAuth.uiFlowBitfeild |= 1; //turn on the first bit - snprintf(state.txnAuth.appendagesText, sizeof(state.txnAuth.appendagesText), "0x%08X", appendages); - } - - return R_SUCCESS; -} - -//Parses all the bytes until the endof the txn, since we don't parse the specifics of all the types, sometimes this is needed -uint8_t parseIngoreBytesUntilTheEnd() { - while (state.txnAuth.numBytesRead != state.txnAuth.txnSizeBytes) { - if (0 == readFromBuffer(1)) + ptr = readFromBuffer(1 + 8); // version plus 1 long + if (ptr == 0) return R_SEND_MORE_BYTES; + ptr += 1; //version + os_memmove(&(state.txnAuth.attachmentTempInt64Num1), ptr, sizeof(state.txnAuth.attachmentTempInt64Num1)); // assetID + ptr += sizeof(state.txnAuth.attachmentTempInt64Num1); + + // Window 1 is order ID + snprintf(state.txnAuth.optionalWindow1Title, sizeof(state.txnAuth.optionalWindow1Title), "%s", "Order ID"); + pos = formatAmount(state.txnAuth.optionalWindow1Text, sizeof(state.txnAuth.optionalWindow1Text), state.txnAuth.attachmentTempInt64Num1, 0); + if (0 == pos) + return R_FORMAT_AMOUNT_ERR; + + break; + case 0x1016: + txTypeText = "Create Contract"; + + break; + default: + return R_UNSUPPORTED_APPENDAGE; } - return R_SUCCESS; -} - -//Parses a specific type of attachment -uint8_t parseFxtCoinExchangeOrderIssueOrCoinExchangeOrderIssueAttachment() { - - state.txnAuth.attachmentTempInt32Num1 = 0; //chaidId - state.txnAuth.attachmentTempInt32Num2 = 0; //exchangeChain - state.txnAuth.attachmentTempInt64Num1 = 0; //quantity - state.txnAuth.attachmentTempInt64Num2 = 0; //price - - uint8_t * ptr = readFromBuffer(sizeof(uint8_t) + sizeof(state.txnAuth.attachmentTempInt32Num1) * 2 + sizeof(state.txnAuth.attachmentTempInt64Num1) * 2); - if (0 == ptr) - return R_SEND_MORE_BYTES; - - if (1 != *ptr) - return R_UNSUPPORTED_ATTACHMENT_VERSION; - - ptr += 1; - - os_memmove(&state.txnAuth.attachmentTempInt32Num1, ptr, sizeof(state.txnAuth.attachmentTempInt32Num1)); - ptr += sizeof(state.txnAuth.attachmentTempInt32Num1); - - if (NUM_CHAINS < state.txnAuth.attachmentTempInt32Num1) - return R_BAD_CHAIN_ID_ERR; - - os_memmove(&state.txnAuth.attachmentTempInt32Num2, ptr, sizeof(state.txnAuth.attachmentTempInt32Num2)); - ptr += sizeof(state.txnAuth.attachmentTempInt32Num2); - - if (NUM_CHAINS < state.txnAuth.attachmentTempInt32Num2) - return R_BAD_CHAIN_ID_ERR; - - os_memmove(&state.txnAuth.attachmentTempInt64Num1, ptr, sizeof(state.txnAuth.attachmentTempInt64Num1)); - ptr += sizeof(state.txnAuth.attachmentTempInt64Num1); - - os_memmove(&state.txnAuth.attachmentTempInt64Num2, ptr, sizeof(state.txnAuth.attachmentTempInt64Num2)); + snprintf(state.txnAuth.txnTypeText, sizeof(state.txnAuth.txnTypeText), "%s", txTypeText); return R_SUCCESS; } -//Parses a specific type of attachment -uint8_t parseAskOrderPlacementAttachment() { - - state.txnAuth.attachmentTempInt64Num1 = 0; - state.txnAuth.attachmentTempInt64Num2 = 0; - state.txnAuth.attachmentTempInt64Num3 = 0; +//Parses a txn reference, by just skiping over the bytes :) +uint8_t parseReferencedTxn() { - uint8_t * ptr = readFromBuffer(sizeof(state.txnAuth.attachmentTempInt64Num1) * 3); - if (0 == ptr) + if (0 == readFromBuffer(sizeof(uint32_t) + 32)) return R_SEND_MORE_BYTES; - os_memmove(&state.txnAuth.attachmentTempInt64Num1, ptr, sizeof(state.txnAuth.attachmentTempInt64Num1)); - ptr += sizeof(state.txnAuth.attachmentTempInt64Num1); - - os_memmove(&state.txnAuth.attachmentTempInt64Num2, ptr, sizeof(state.txnAuth.attachmentTempInt64Num2)); - ptr += sizeof(state.txnAuth.attachmentTempInt64Num2); - - os_memmove(&state.txnAuth.attachmentTempInt64Num3, ptr, sizeof(state.txnAuth.attachmentTempInt64Num3)); - return R_SUCCESS; } @@ -642,96 +533,32 @@ uint8_t addToReadBuffer(const uint8_t * const newData, const uint8_t numBytes) { return R_SUCCESS; } -//Since we can't store function pointers in the functionstack, we store number and then call the following function -//to make a call to the corresponding function -uint8_t callFunctionNumber(const uint8_t functionNum) { - - switch (functionNum) { - case 1: - return parseMainTxnData(); - case 2: - return parseAppendagesFlags(); - case 3: - return parseReferencedTxn(); - case 4: - return parseFxtCoinExchangeOrderIssueOrCoinExchangeOrderIssueAttachment(); - case 5: - return parseAskOrderPlacementAttachment(); - case 6: - return parseIngoreBytesUntilTheEnd(); - } - - return R_PARSE_FUNCTION_NOT_FOUND; -} - -//This function manages the parsing of the readBuffer with functionStack functions -//If there aren't enough bytes in the read buffer it returns R_SEND_MORE_BYTES -//which will be sent back to the user -uint8_t parseFromStack() { - - while (true) { - - if (0 == state.txnAuth.numFunctionsOnStack) { - - if (state.txnAuth.readBufferEndPos != state.txnAuth.readBufferReadOffset) - return R_NOT_ALL_BYTES_READ; - - uint8_t ret = setScreenTexts(); - - if (R_SUCCESS != ret) - return ret; - - showScreen(); - - return R_SHOW_DISPLAY; - } - - uint8_t ret = callFunctionNumber(state.txnAuth.functionStack[0]); - - if (R_SEND_MORE_BYTES == ret) - return ret; - - uint8_t tempBuffer[FUNCTION_STACK_SIZE - 1]; - os_memmove(tempBuffer, state.txnAuth.functionStack + 1, sizeof(tempBuffer)); - os_memmove(state.txnAuth.functionStack, tempBuffer, sizeof(tempBuffer)); - state.txnAuth.functionStack[sizeof(state.txnAuth.functionStack) - 1] = 0; - state.txnAuth.numFunctionsOnStack--; - - if (R_SUCCESS == ret) - continue; - - return ret; - } -} - - //This is the function used to sign the hash of the txn //@param txnSha256 - ptr to 32 byte sha256 of the txn -//@param derivationPath - ptr to the derivation path buffer -//@param derivationPathLengthInUints32 - length of the derivation path buffer //@param destBuffer - ptr to 64 bytes of memory of where to write the buffer //@param outException out - ptr to where to write the exception if it happends -//@returns R_SUCCESS iff success else the appropriate error code is returned +//@returns R_SUCCESS if success else the appropriate error code is returned -uint8_t signTxn(const uint8_t * const derivationPath, const uint8_t derivationPathLengthInUints32, - uint8_t * const destBuffer, uint16_t * const outException) { +uint8_t signTxn(uint8_t index, uint8_t * const destBuffer, uint16_t * const outException) { - uint8_t keySeed[32]; os_memset(keySeed, 0, sizeof(keySeed)); + uint8_t sharedKey[32]; os_memset(sharedKey, 0, sizeof(sharedKey)); uint8_t ret = 0; - if (R_SUCCESS != (ret = ardorKeys(derivationPath, derivationPathLengthInUints32, keySeed, 0, 0, 0, outException))) { - os_memset(keySeed, 0, sizeof(keySeed)); + if (R_SUCCESS != (ret = burstKeys(index, NULL, NULL, sharedKey, outException))) { + explicit_bzero(sharedKey, sizeof(sharedKey)); return ret; } - uint8_t finalTxnSha256[32]; - cx_hash(&state.txnAuth.hashstate.header, CX_LAST, 0, 0, finalTxnSha256, sizeof(finalTxnSha256)); + // Get the message hash + uint8_t messageSha256[32]; + cx_hash(&state.txnAuth.hashstate.header, CX_LAST, NULL, 0, messageSha256, sizeof(messageSha256)); - //sign msg should only use the first 32 bytes of keyseed - signMsg(keySeed, finalTxnSha256, destBuffer); //is a void function, no ret value to check against + signMsg(sharedKey, messageSha256, destBuffer); //is a void function, no ret value to check against + //os_memcpy(destBuffer+32, messageSha256, 32); - os_memset(finalTxnSha256, 0, sizeof(finalTxnSha256)); //for security - os_memset(keySeed, 0, sizeof(keySeed)); + //clear + explicit_bzero(messageSha256, sizeof(messageSha256)); + explicit_bzero(sharedKey, sizeof(sharedKey)); return R_SUCCESS; } @@ -744,29 +571,15 @@ uint8_t signTxn(const uint8_t * const derivationPath, const uint8_t derivationPa void authAndSignTxnHandlerHelper(const uint8_t p1, const uint8_t p2, const uint8_t * const dataBuffer, const uint8_t dataLength, uint8_t * const flags, uint8_t * const tx, const bool isLastCommandDifferent) { - if (dataLength < 1) { - initTxnAuthState(); - G_io_apdu_buffer[(*tx)++] = R_WRONG_SIZE_ERR; - return; - } - + if (P1_SIGN_FINISH == (p1 & 0x03)) { + // Sign a transaction initiated before with P1_SIGN_INIT (and possibly P1_SIGN_CONTINUE) - if (P1_SIGN == (p1 & 0x03)) { - - if (isLastCommandDifferent) { + if (isLastCommandDifferent || state.txnAuth.isClean) { initTxnAuthState(); - G_io_apdu_buffer[(*tx)++] = R_TXN_UNAUTHORIZED; + G_io_apdu_buffer[(*tx)++] = R_ERR_NO_INIT_CANT_CONTINUE; return; } - uint8_t derivationParamLengthInBytes = dataLength; - - if ((MIN_DERIVATION_LENGTH * sizeof(uint32_t) > dataLength) || (MAX_DERIVATION_LENGTH * sizeof(uint32_t) < dataLength) || (0 != derivationParamLengthInBytes % sizeof(uint32_t))) { - initTxnAuthState(); - G_io_apdu_buffer[(*tx)++] = R_WRONG_SIZE_ERR; - return; - } - if (!state.txnAuth.txnPassedAutherization) { initTxnAuthState(); G_io_apdu_buffer[(*tx)++] = R_TXN_UNAUTHORIZED; @@ -775,28 +588,21 @@ void authAndSignTxnHandlerHelper(const uint8_t p1, const uint8_t p2, const uint8 uint16_t exception = 0; - G_io_apdu_buffer[(*tx)++] = R_SUCCESS; - - uint8_t ret = signTxn(dataBuffer, derivationParamLengthInBytes / 4, G_io_apdu_buffer + 1, &exception); + uint8_t ret = signTxn(p2, G_io_apdu_buffer + 1, &exception); + // clear the state for a future call initTxnAuthState(); if (R_SUCCESS == ret) { + G_io_apdu_buffer[(*tx)++] = R_SUCCESS; *tx += 64; } else { - *tx -= 1; G_io_apdu_buffer[(*tx)++] = ret; - - if (R_KEY_DERIVATION_EX == ret) { - G_io_apdu_buffer[(*tx)++] = exception >> 8; - G_io_apdu_buffer[(*tx)++] = exception & 0xFF; - } } - } else if ((P1_INIT == (p1 & 0x03)) || (P1_CONTINUE == (p1 & 0x03))) { - - if (P1_INIT != (p1 & 0x03)) { + } else if ((P1_SIGN_INIT == (p1 & 0x03)) || (P1_SIGN_CONTINUE == (p1 & 0x03))) { - if (isLastCommandDifferent) { + if (P1_SIGN_CONTINUE == (p1 & 0x03)) { + if (isLastCommandDifferent || state.txnAuth.isClean) { initTxnAuthState(); G_io_apdu_buffer[(*tx)++] = R_ERR_NO_INIT_CANT_CONTINUE; return; @@ -808,17 +614,13 @@ void authAndSignTxnHandlerHelper(const uint8_t p1, const uint8_t p2, const uint8 return; } - if (state.txnAuth.isClean) { - initTxnAuthState(); - G_io_apdu_buffer[(*tx)++] = R_ERR_NO_INIT_CANT_CONTINUE; - return; - } } else { + // P1_SIGN_INIT initTxnAuthState(); state.txnAuth.txnSizeBytes = ((p1 & 0b11111100) << 6) + p2; - if (145 > state.txnAuth.txnSizeBytes) { + if (state.txnAuth.txnSizeBytes < 176) { G_io_apdu_buffer[(*tx)++] = R_TXN_SIZE_TOO_SMALL; return; } @@ -828,15 +630,19 @@ void authAndSignTxnHandlerHelper(const uint8_t p1, const uint8_t p2, const uint8 uint8_t ret = addToReadBuffer(dataBuffer, dataLength); - if (R_SUCCESS != ret) { - G_io_apdu_buffer[(*tx)++] = ret; - return; + // parse the transaction data, and setup screens + if(ret == R_SUCCESS) + ret = parseMainTxnData(); + if(ret == R_SUCCESS){ + showScreen(); + ret = R_SHOW_DISPLAY; } - ret = parseFromStack(); - - if (!((R_SEND_MORE_BYTES == ret) || (R_FINISHED == ret) || (R_SHOW_DISPLAY == ret))) + if (!((R_SEND_MORE_BYTES == ret) || (R_FINISHED == ret) || (R_SHOW_DISPLAY == ret))){ initTxnAuthState(); + G_io_apdu_buffer[(*tx)++] = ret; + return; + } if (R_SHOW_DISPLAY == ret) { *flags |= IO_ASYNCH_REPLY; diff --git a/src/burst.c b/src/burst.c new file mode 100644 index 0000000..abb2224 --- /dev/null +++ b/src/burst.c @@ -0,0 +1,204 @@ +/******************************************************************************* +* (c) 2019 Haim Bender +* +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +********************************************************************************/ + +#include +#include + +#include +#include +#include + +#include "curve25519_i64.h" +#include "returnValues.h" +#include "config.h" + +#include "burst.h" + + + +//the global state +states_t state; + + +//self explanatory +//output must point to buffer of 32 bytes in size +void sha256TwoBuffers(const uint8_t * const bufferTohash1, const uint16_t sizeOfBuffer1, const uint8_t * const bufferTohash2, const uint16_t sizeOfBuffer2, uint8_t * const output) { + cx_sha256_t shaContext; + + os_memset(output, 0, 32); + cx_sha256_init(&shaContext); //return value has no info + + cx_hash(&shaContext.header, 0, bufferTohash1, sizeOfBuffer1, output, 32); + + if (0 != bufferTohash2) + cx_hash(&shaContext.header, 0, bufferTohash2, sizeOfBuffer2, output, 32); + + cx_hash(&shaContext.header, CX_LAST, 0, 0, output, 32); +} + +//self explanatory +//output must point to buffer of 32 bytes in size +void sha256Buffer(const uint8_t * const bufferTohash, const uint16_t sizeOfBuffer, uint8_t * const output) { + sha256TwoBuffers(bufferTohash, sizeOfBuffer, 0, 0, output); +} + +// Sign implementation for Burst, assumes message data was aleady added by cx_hash +//@param in: sharedKey private key for signing +//@parma in: msgSha256 should point to a 32 byte sha256 of the message we are signing +//@param out: sig should point to 64 bytes allocated to hold the signiture of the message +void signMsg(uint8_t * const sharedKey, const uint8_t * const msgSha256, uint8_t * const sig) { + + uint8_t x[32]; os_memset(x, 0, sizeof(x)); + uint8_t y[32]; os_memset(y, 0, sizeof(y)); + uint8_t h[32]; os_memset(h, 0, sizeof(h)); + + // Get x = hash(m, s) + cx_sha256_init(&state.txnAuth.hashstate); + cx_hash(&state.txnAuth.hashstate.header, 0, msgSha256, 32, NULL, 0); + cx_hash(&state.txnAuth.hashstate.header, CX_LAST, sharedKey, 32, x, 32); + + // get y through + keygen25519(y, NULL, x); + + // h = hash(m, y); + cx_sha256_init(&state.txnAuth.hashstate); + cx_hash(&state.txnAuth.hashstate.header, 0, msgSha256, 32, NULL, 0); + cx_hash(&state.txnAuth.hashstate.header, CX_LAST, y, 32, h, 32); + + // copy h first because sign25519 screws with parameters + os_memcpy(sig+32, h, 32); + sign25519(sig, h, x, sharedKey); + + // clear sensitive data + explicit_bzero(h, sizeof(h)); + explicit_bzero(sharedKey, sizeof(sharedKey)); +} + + +//from curveConversion.C +void morph25519_e2m(uint8_t *montgomery, const uint8_t *y); + + +//this function derives an burst private key, public key, public key and shared key (for signing) +//For more info on how this derivation works, please read the readme +//@param in: derivationPath - a BIP42 derivation path, must be at least of length MIN_DERIVATION_PATH_LENGTH +//@param optional out: keySeedBfrOut - 32 byte EC-KCDSA keyseed for the derivation path +//@param optional out: publicKeyOut - 32 byte EC-KCDSA public key for the derivation path +//@param optional out: sharedKeyOut +//@param out: exceptionOut - iff the return code is R_EXCEPTION => exceptionOut will be filled with the Nano exception code +//@returns: regular return values +uint8_t burstKeys(const uint8_t index, uint8_t * const privKeyOut, uint8_t * const publicKeyOut, + uint8_t * const sharedKeyOut, uint16_t * const exceptionOut) { + + uint32_t pathPrefix[] = PATH_PREFIX; //defined in Makefile + uint8_t account = 0; + uint8_t change = 0; + + uint8_t publicKey[32]; os_memset(publicKey, 0, sizeof(publicKey)); + uint8_t privKey[32]; os_memset(privKey, 0, sizeof(privKey)); + + // BURST keypath of 44'/30'/0'/0'/index' + uint32_t derivationPath[5]; os_memset(derivationPath, 0, sizeof(derivationPath)); + os_memmove(derivationPath, pathPrefix, 2 * sizeof(uint32_t)); + derivationPath[2] = account | 0x80000000; + derivationPath[3] = change | 0x80000000; + derivationPath[4] = index | 0x80000000; + + BEGIN_TRY { + TRY { + os_perso_derive_node_bip32(CX_CURVE_Ed25519, derivationPath, 5, privKey, NULL); + + if (0 != publicKeyOut || 0 != sharedKeyOut) { + keygen25519(publicKey, sharedKeyOut, privKey); + } + + if (0 != publicKeyOut) { + os_memmove(publicKeyOut, publicKey, 32); + } + if (0 != privKeyOut) { + os_memmove(privKeyOut, privKey, 32); + } + } + CATCH_OTHER(exception) { + *exceptionOut = exception; + return R_KEY_DERIVATION_EX; + } + FINALLY { + explicit_bzero(privKey, sizeof(privKey)); + explicit_bzero(publicKey, sizeof(publicKey)); + } + } + END_TRY; + + return R_SUCCESS; +} + +//Creates a shared AES encryption key one the matches the key related to the derivation path, the target public key and the nonce +//@param derivationPath - the derivation path +//@param derivationPathLengthInUints32 - kinda clear what this is +//@param targetPublicKey - the 32 byte public key +uint8_t getSharedEncryptionKey(const uint8_t index, const uint8_t* const targetPublicKey, + const uint8_t * const nonce, uint16_t * const exceptionOut, uint8_t * const aesKeyOut) { + + uint8_t keySeed[32]; os_memset(keySeed, 0, sizeof(keySeed)); + + uint8_t ret = burstKeys(index, keySeed, 0, 0, exceptionOut); + + if (R_SUCCESS != ret) + return ret; + + uint8_t sharedKey[32]; os_memset(sharedKey, 0, sizeof(sharedKey)); + + + curve25519(sharedKey, keySeed, targetPublicKey); //should use only the first 32 bytes of keyseed + + for (uint8_t i = 0; i < sizeof(sharedKey); i++) + sharedKey[i] ^= nonce[i]; + + sha256Buffer(sharedKey, sizeof(sharedKey), aesKeyOut); + + return R_SUCCESS; +} + +//param: publicKey should point to a 32 byte public key buffer +//returns: a 64bit public key id, used later with reedsolomon to create BURST addresses +uint64_t publicKeyToId(const uint8_t * const publicKey) { + + uint8_t tempSha[32]; + sha256Buffer(publicKey, 32, tempSha); + + return ((((uint64_t) tempSha[7]) << 56) | + (((uint64_t) tempSha[6]) << 48) | + (((uint64_t) tempSha[5]) << 40) | + (((uint64_t) tempSha[4]) << 32) | + (((uint64_t) tempSha[3]) << 24) | + (((uint64_t) tempSha[2]) << 16) | + (((uint64_t) tempSha[1]) << 8) | + (((uint64_t) tempSha[0] ))); +} + + +//app_stack_canary is defined by the link script to be at the start of the user data or end of the stack, something like that +//so if there is a stack overflow then it will be overwriten, this is how check_canary() works. +//make sure HAVE_BOLOS_APP_STACK_CANARY is defined in the makefile, so that the OS code will init it and check against it every io_exchange call +//if the canary is not the same, and if not, it will throw + +extern unsigned int app_stack_canary; + +bool check_canary() { + return 0xDEAD0031 == app_stack_canary; +} diff --git a/src/ardor.h b/src/burst.h similarity index 74% rename from src/ardor.h rename to src/burst.h index efc8fa4..91a09c2 100644 --- a/src/ardor.h +++ b/src/burst.h @@ -20,15 +20,15 @@ #endif uint64_t publicKeyToId(const uint8_t * const publicKey); -uint8_t ardorKeys(const uint8_t * const derivationPath, const uint8_t derivationPathLengthInUints32, - uint8_t * const keySeedBfrOut, uint8_t * const publicKeyCurveXout, uint8_t * const publicKeyEd25519YLEWithXParityOut, uint8_t * const chainCodeOut, uint16_t * const exceptionOut); +uint8_t burstKeys(const uint8_t index, uint8_t * const privKeyOut, uint8_t * const publicKeyOut, + uint8_t * const sharedKeyOut, uint16_t * const exceptionOut); -void signMsg(uint8_t * const keySeedBfr, const uint8_t * const msgSha256, uint8_t * const sig); +void signMsg(uint8_t * const sharedKey, const uint8_t * const msgSha256, uint8_t * const sig); void ui_idle(); bool check_canary(); -uint8_t getSharedEncryptionKey(const uint8_t * const derivationPath, const uint8_t derivationPathLengthInUints32, const uint8_t* const targetPublicKey, +uint8_t getSharedEncryptionKey(const uint8_t index, const uint8_t* const targetPublicKey, const uint8_t * const nonce, uint16_t * const exceptionOut, uint8_t * const aesKeyOut); @@ -42,21 +42,15 @@ typedef struct { uint16_t readBufferReadOffset; //Index of the first byte in readBuffer uint16_t numBytesRead; //The total number of bytes parsed up until now - uint8_t functionStack[FUNCTION_STACK_SIZE]; //This is stack of all the function that have yet to parse the TXN, the C handler file explains this process in more detail - uint8_t numFunctionsOnStack; //Is what it says - bool isClean; //If the state was just initilized cx_sha256_t hashstate; //The state of the hash for the txn buffer - uint32_t chainId; //What it says it is uint16_t txnTypeAndSubType; //What it says it is - uint8_t txnTypeIndex; //txnTypeAndSubType's index in TXN_TYPES uint64_t recipientId; //the recipient address ID uint64_t amount; //the amount to be sent in the txn, note that every chain parses this number differently, it dives this number by some 10^X uint64_t fee; //What it says it is - //What it says it is int32_t attachmentTempInt32Num1, attachmentTempInt32Num2; //Different attachments parse in different ways, they all need space in state, so this is how it's defined int64_t attachmentTempInt64Num1, attachmentTempInt64Num2, attachmentTempInt64Num3; @@ -65,12 +59,14 @@ typedef struct { char feeText[21]; //9,223,372,036,854,775,807 is the biggest number you can hold in uint64 + the dot + null terminator means the longest text is 20 - char chainAndTxnTypeText[60]; //Aproximation of size + char txnTypeText[60]; //Aproximation of size + char optionalWindow1Title[20]; //The longest string is price per (some chain name here) char optionalWindow1Text[31]; //same as fee text + name of the chain + space char optionalWindow2Title[20]; //The longest string is price per (some chain name here) - char optionalWindow2Text[31]; //MAX(Ardor arddress = 27, feeText + chainName) - char appendagesText[11]; //0x and then 8 chars - uint8_t uiFlowBitfeild; //This is a bit feild for selecting the right UI flow + char optionalWindow2Text[31]; //MAX(Burst arddress = 27, feeText + chainName) + char appendagesTitle[20]; //0x and then 8 chars + char appendagesText[31]; //0x and then 8 chars + const ux_flow_step_t* const * ux_flow; //The ux flow to be used } authTxn_t; @@ -87,14 +83,14 @@ typedef struct { cx_sha256_t sha256; //The state of the token hash } signTokenState_t; -//This is the union states type, the actual object is defined in ardor.c +//This is the union states type, the actual object is defined in burst.c typedef union { encyptionState_t encryption; authTxn_t txnAuth; signTokenState_t tokenCreation; } states_t; -//declared in ardor.c +//declared in burst.c extern states_t state; //used to list txn types @@ -105,14 +101,10 @@ typedef struct { } txnType; -//These to are automaticly generated by createTxnTypes.py into src/txnTypeLists.c -extern const txnType TXN_TYPES[]; -extern const uint8_t LEN_TXN_TYPES; - // These are the offsets of various parts of a request APDU packet. INS // identifies the requested command (see above), and P1 and P2 are parameters // to the command. -#define CLA 0xE0 +#define CLA 0x80 #define OFFSET_CLA 0x00 #define OFFSET_INS 0x01 #define OFFSET_P1 0x02 diff --git a/src/config.c b/src/config.c index f2ccae3..c9a063e 100644 --- a/src/config.c +++ b/src/config.c @@ -8,14 +8,9 @@ #include "config.h" - -//This configures the supported chain types, chainId, name and amount of decimals to the right of the point -const chainType CHAINS[] = {{0x00000001, "Ardor", 8}, {0x00000002, "Ignis", 8}, {0x00000003, "AEUR", 4}, {0x00000004, "BITS", 8}, {0x00000005, "MPG", 8}}; -const uint8_t NUM_CHAINS = sizeof(CHAINS) / sizeof(CHAINS[0]); - const uint8_t SUPPORTED_TXN_VERSION = 1; -const uint8_t ARDOR_SPECIAL_IDENTIFIER[] = {0xba, 0xbe, 0x00}; -const uint8_t ARDOR_SPECIAL_IDENTIFIER_LEN = sizeof(ARDOR_SPECIAL_IDENTIFIER); +const uint8_t BURST_SPECIAL_IDENTIFIER[] = {0xba, 0xbb, 0xbc}; +const uint8_t BURST_SPECIAL_IDENTIFIER_LEN = sizeof(BURST_SPECIAL_IDENTIFIER); const uint8_t VERSION_FLAGS = 0x00; diff --git a/src/config.h b/src/config.h index f47a541..c70e7b3 100644 --- a/src/config.h +++ b/src/config.h @@ -9,20 +9,15 @@ typedef struct { uint8_t numDecimalsBeforePoint; } chainType; -extern const chainType CHAINS[]; -extern const uint8_t NUM_CHAINS; extern const uint8_t SUPPORTED_TXN_VERSION; -extern const uint8_t ARDOR_SPECIAL_IDENTIFIER[]; -extern const uint8_t ARDOR_SPECIAL_IDENTIFIER_LEN; +extern const uint8_t BURST_SPECIAL_IDENTIFIER[]; +extern const uint8_t BURST_SPECIAL_IDENTIFIER_LEN; extern const uint16_t VERSION; extern const uint8_t VERSION_FLAGS; - -//must make this a define instead of static const, because some array declirations are dependant on this size -#define MIN_DERIVATION_LENGTH 3 -#define MAX_DERIVATION_LENGTH 20 - #define FUNCTION_STACK_SIZE 30 #define IV_SIZE 16 + +#define TRT_TOKEN -6044328578714302076L diff --git a/src/encryptDecryptMsg.c b/src/encryptDecryptMsg.c index d5ffef9..9a8dd42 100644 --- a/src/encryptDecryptMsg.c +++ b/src/encryptDecryptMsg.c @@ -27,7 +27,7 @@ #include "returnValues.h" #include "config.h" -#include "ardor.h" +#include "burst.h" #define P1_INIT_ENCRYPT 1 @@ -77,7 +77,6 @@ void aes_encrypt(void *ctx, const aes_uchar *plain, aes_uchar *crypt); void encryptDecryptMessageHandlerHelper(const uint8_t p1, const uint8_t p2, const uint8_t * const dataBuffer, const uint8_t dataLength, uint8_t * const flags, uint8_t * const tx, const bool isLastCommandDifferent) { - UNUSED(p2); UNUSED(flags); if (isLastCommandDifferent) @@ -91,23 +90,8 @@ void encryptDecryptMessageHandlerHelper(const uint8_t p1, const uint8_t p2, cons return; } - int16_t derivationLengthSigned = 0; - - if (P1_INIT_ENCRYPT == p1) - derivationLengthSigned = (dataLength - 32) / sizeof(uint32_t); //no underflow because type is signed - else - derivationLengthSigned = (dataLength - 32 * 2 - 16) / sizeof(uint32_t); - - if ((MIN_DERIVATION_LENGTH > derivationLengthSigned) || (MAX_DERIVATION_LENGTH < derivationLengthSigned)) { - cleanEncryptionState(); - G_io_apdu_buffer[(*tx)++] = R_WRONG_SIZE_ERR; - return; - } - - uint8_t derivationLength = derivationLengthSigned; //cast is ok, because if the check above - uint8_t nonce[32]; - const uint8_t * noncePtr = dataBuffer + derivationLength * sizeof(uint32_t) + 32; + const uint8_t * noncePtr = dataBuffer + 32; if (P1_INIT_ENCRYPT == p1) { cx_rng(nonce, sizeof(nonce)); @@ -117,7 +101,7 @@ void encryptDecryptMessageHandlerHelper(const uint8_t p1, const uint8_t p2, cons uint16_t exceptionOut = 0; uint8_t encryptionKey[32]; - uint8_t ret = getSharedEncryptionKey(dataBuffer, derivationLength, dataBuffer + derivationLength * sizeof(uint32_t), noncePtr, &exceptionOut, encryptionKey); + uint8_t ret = getSharedEncryptionKey(p2, dataBuffer, noncePtr, &exceptionOut, encryptionKey); if (R_KEY_DERIVATION_EX == ret) { cleanEncryptionState(); diff --git a/src/getPublicKeyAndChaincode.c b/src/getPublicKeyAndChaincode.c index 84b8a1e..0a71a9a 100644 --- a/src/getPublicKeyAndChaincode.c +++ b/src/getPublicKeyAndChaincode.c @@ -24,10 +24,7 @@ #include "returnValues.h" #include "config.h" -#include "ardor.h" - -#define P1_GET_PUBLIC_KEY 1 -#define P1_GET_PUBLIC_KEY_CHAIN_CODE_AND_ED_PUBLIC_KEY 2 +#include "burst.h" /* @@ -46,55 +43,23 @@ */ - -void getPublicKeyAndChainCodeHandlerHelper(const uint8_t p1, const uint8_t p2, const uint8_t * const dataBuffer, const uint8_t dataLength, +void getPublicKeyHandlerHelper(const uint8_t p1, const uint8_t p2, const uint8_t * const dataBuffer, const uint8_t dataLength, uint8_t * const flags, uint8_t * const tx) { - UNUSED(p2); UNUSED(flags); - - if ((P1_GET_PUBLIC_KEY != p1) && (P1_GET_PUBLIC_KEY_CHAIN_CODE_AND_ED_PUBLIC_KEY != p1)) { - G_io_apdu_buffer[(*tx)++] = R_UNKNOWN_CMD_PARAM_ERR; - return; - } - - if ((MIN_DERIVATION_LENGTH * sizeof(uint32_t) > dataLength) || (MAX_DERIVATION_LENGTH * sizeof(uint32_t) < dataLength)) { - G_io_apdu_buffer[(*tx)++] = R_WRONG_SIZE_ERR; - return; - } - - uint8_t derivationParamLengthInBytes = dataLength; + UNUSED(p1); UNUSED(flags); - if (0 != derivationParamLengthInBytes % sizeof(uint32_t)) { - G_io_apdu_buffer[(*tx)++] = R_UNKNOWN_CMD_PARAM_ERR; - return; - } - - uint8_t publicKeyEd25519YLE[32]; - uint8_t publicKeyCurve[32]; - uint8_t chainCode[32]; - //uint8_t K[64]; //DO NOT COMMIT THIS LINE!!! DO NOT COMMIT THIS LINE!!!!, used for testing only, to send the privatekey to the client, private key should never be released + uint8_t publicKey[32]; uint16_t exception = 0; - uint8_t ret = ardorKeys(dataBuffer, derivationParamLengthInBytes / sizeof(uint32_t), 0, publicKeyCurve, publicKeyEd25519YLE, chainCode, &exception); - //uint8_t ret = ardorKeys(derivationPathCpy, derivationParamLengthInBytes / sizeof(uint32_t), K, publicKeyCurve, publicKeyEd25519YLE, chainCode, &exception); //DO NOT COMMIT THIS LINE!!! DO NOT COMMIT THIS LINE!!!!, used for testing only, to send the privatekey to the client, private key should never be released + uint8_t ret = burstKeys(p2, 0, publicKey, 0, &exception); + // uint8_t ret = burstKeys(p2, publicKey, 0, 0, &exception); // DO NOT COMMIT THIS LINE!!!, used for testing only, to send the privatekey to the client, private key should never be released G_io_apdu_buffer[(*tx)++] = ret; if (R_SUCCESS == ret) { - os_memmove(G_io_apdu_buffer + *tx, publicKeyCurve, sizeof(publicKeyCurve)); - *tx += sizeof(publicKeyCurve); - - if (P1_GET_PUBLIC_KEY_CHAIN_CODE_AND_ED_PUBLIC_KEY == p1) { - os_memmove(G_io_apdu_buffer + *tx, publicKeyEd25519YLE, sizeof(publicKeyEd25519YLE)); - *tx += sizeof(publicKeyEd25519YLE); - - os_memmove(G_io_apdu_buffer + *tx, chainCode, sizeof(chainCode)); - *tx += sizeof(chainCode); - - //os_memmove(G_io_apdu_buffer + *tx, K, sizeof(K)); //DO NOT COMMIT THIS LINE!!! DO NOT COMMIT THIS LINE!!!!, used for testing only, to send the privatekey to the client, private key should never be released - //*tx += sizeof(K); //DO NOT COMMIT THIS LINE!!! DO NOT COMMIT THIS LINE!!!!, used for testing only, to send the privatekey to the client, private key should never be released - } + os_memmove(G_io_apdu_buffer + *tx, publicKey, sizeof(publicKey)); + *tx += sizeof(publicKey); } else if (R_KEY_DERIVATION_EX == ret) { G_io_apdu_buffer[(*tx)++] = exception >> 8; @@ -102,12 +67,12 @@ void getPublicKeyAndChainCodeHandlerHelper(const uint8_t p1, const uint8_t p2, c } } -void getPublicKeyAndChainCodeHandler(const uint8_t p1, const uint8_t p2, const uint8_t * const dataBuffer, const uint8_t dataLength, +void getPublicKeyHandler(const uint8_t p1, const uint8_t p2, const uint8_t * const dataBuffer, const uint8_t dataLength, uint8_t * const flags, uint8_t * const tx, const bool isLastCommandDifferent) { UNUSED(isLastCommandDifferent); //there is no state to manage, so there's nothing to do with this parameter - getPublicKeyAndChainCodeHandlerHelper(p1, p2, dataBuffer, dataLength, flags, tx); + getPublicKeyHandlerHelper(p1, p2, dataBuffer, dataLength, flags, tx); G_io_apdu_buffer[(*tx)++] = 0x90; G_io_apdu_buffer[(*tx)++] = 0x00; diff --git a/src/getVersion.c b/src/getVersion.c index b9addb4..b3fcec5 100644 --- a/src/getVersion.c +++ b/src/getVersion.c @@ -22,8 +22,8 @@ #include "config.h" -//function that returns the version, in order to see if this is actually the ardor app -//returns VERSION 2 bytes | FLAGS 1 byte | ARDOR_SPECIAL_IDENTIFIER 3 bytes +//function that returns the version, in order to see if this is actually the burst app +//returns VERSION 2 bytes | FLAGS 1 byte | BURST_SPECIAL_IDENTIFIER 3 bytes void getVersionHandler(const uint8_t p1, const uint8_t p2, const uint8_t * const dataBuffer, const uint8_t dataLength, uint8_t * const flags, uint8_t * const tx, const bool isLastCommandDifferent) { @@ -34,8 +34,8 @@ void getVersionHandler(const uint8_t p1, const uint8_t p2, const uint8_t * const G_io_apdu_buffer[(*tx)++] = APPVERSION_P; G_io_apdu_buffer[(*tx)++] = VERSION_FLAGS; - os_memmove(G_io_apdu_buffer + (*tx), ARDOR_SPECIAL_IDENTIFIER, ARDOR_SPECIAL_IDENTIFIER_LEN); - *tx += ARDOR_SPECIAL_IDENTIFIER_LEN; + os_memmove(G_io_apdu_buffer + (*tx), BURST_SPECIAL_IDENTIFIER, BURST_SPECIAL_IDENTIFIER_LEN); + *tx += BURST_SPECIAL_IDENTIFIER_LEN; G_io_apdu_buffer[(*tx)++] = 0x90; G_io_apdu_buffer[(*tx)++] = 0x00; diff --git a/src/main.c b/src/main.c index ab1e701..37e41cd 100644 --- a/src/main.c +++ b/src/main.c @@ -25,7 +25,7 @@ #include "ux.h" #include "config.h" -#include "ardor.h" +#include "burst.h" #include "returnValues.h" ux_state_t G_ux; @@ -33,7 +33,7 @@ bolos_ux_params_t G_ux_params; UX_STEP_NOCB( ux_idle_flow_1_step, - bn, + nn, { "Application", "is ready", @@ -79,7 +79,7 @@ void ui_idle() { #define INS_AUTH_SIGN_TXN 0x03 #define INS_ENCRYPT_DECRYPT_MSG 0x04 #define INS_SHOW_ADDRESS 0x05 -#define INS_GET_PUBLIC_KEY_AND_CHAIN_CODE 0x06 +#define INS_GET_PUBLIC_KEY 0x06 #define INS_SIGN_TOKEN 0x07 // This is the function signature for a command handler. 'flags' and 'tx' are @@ -90,7 +90,7 @@ handler_fn_t getVersionHandler; handler_fn_t authAndSignTxnHandler; handler_fn_t encryptDecryptMessageHandler; handler_fn_t showAddressHandler; -handler_fn_t getPublicKeyAndChainCodeHandler; +handler_fn_t getPublicKeyHandler; handler_fn_t signTokenMessageHandler; //function translate command ID to function PTR @@ -100,7 +100,7 @@ static handler_fn_t* lookupHandler(uint8_t ins) { case INS_AUTH_SIGN_TXN: return authAndSignTxnHandler; case INS_ENCRYPT_DECRYPT_MSG: return encryptDecryptMessageHandler; case INS_SHOW_ADDRESS: return showAddressHandler; - case INS_GET_PUBLIC_KEY_AND_CHAIN_CODE: return getPublicKeyAndChainCodeHandler; + case INS_GET_PUBLIC_KEY: return getPublicKeyHandler; case INS_SIGN_TOKEN: return signTokenMessageHandler; default: return NULL; } @@ -135,7 +135,7 @@ void fillBufferWithAnswerAndEnding(const uint8_t answer, uint8_t * const tx) { // subsequent io_exchange call. The handler may also throw an exception, which // will be caught, converted to an error code, appended to the response APDU, // and sent in the next io_exchange call. -static void ardor_main(void) { +static void burst_main(void) { lastCmdNumber = 0; @@ -165,7 +165,7 @@ static void ardor_main(void) { // No APDU received; trigger a reset. if (rx == 0) { - THROW(EXCEPTION_IO_RESET); //lastCmdNumber will be zero'd when ardor_main will be called again + THROW(EXCEPTION_IO_RESET); //lastCmdNumber will be zero'd when burst_main will be called again } // Malformed APDU. if (CLA != G_io_apdu_buffer[OFFSET_CLA]) { @@ -182,7 +182,7 @@ static void ardor_main(void) { continue; } - PRINTF("\n canery check %d last command number %d \n", check_canary(), lastCmdNumber); + PRINTF("\n canary check %d last command number %d \n", check_canary(), lastCmdNumber); uint8_t lastCommandSaver = G_io_apdu_buffer[OFFSET_INS]; //the handler is going to write over the buffer, so the command needs to be put aside @@ -192,7 +192,7 @@ static void ardor_main(void) { lastCmdNumber = lastCommandSaver; } CATCH(EXCEPTION_IO_RESET) { - THROW(EXCEPTION_IO_RESET); //lastCmdNumber will be zero'd when ardor_main will be called again + THROW(EXCEPTION_IO_RESET); //lastCmdNumber will be zero'd when burst_main will be called again } CATCH_OTHER(e) { @@ -346,16 +346,14 @@ __attribute__((section(".boot"))) int main(void) { USB_power(1); ui_idle(); - PRINTF("aasdasd ardor"); - + PRINTF("app burst started"); #ifdef HAVE_BLE BLE_power(0, NULL); BLE_power(1, "Nano X"); #endif // HAVE_BLE - ardor_main(); - + burst_main(); } CATCH(EXCEPTION_IO_RESET) { // reset IO and UX before continuing diff --git a/src/returnValues.h b/src/returnValues.h index f2089cc..60dde06 100644 --- a/src/returnValues.h +++ b/src/returnValues.h @@ -32,7 +32,6 @@ enum returnValues { R_SHOW_DISPLAY = 14, R_SEND_MORE_BYTES = 15, R_UNSUPPORTED_APPENDAGE = 16, - R_BAD_CHAIN_ID_ERR = 17, R_WRONG_VERSION_ERR = 18, R_WRONG_SIZE_ERR = 19, R_ERR_NO_INIT_CANT_CONTINUE = 20, diff --git a/src/showAddress.c b/src/showAddress.c index 09fb677..3dfec15 100644 --- a/src/showAddress.c +++ b/src/showAddress.c @@ -25,7 +25,7 @@ #include "returnValues.h" #include "config.h" -#include "ardor.h" +#include "burst.h" //done button callback @@ -76,27 +76,13 @@ void reedSolomonEncode(const uint64_t inp, char * const output); void showAddressHandlerHelper(const uint8_t p1, const uint8_t p2, const uint8_t * const dataBuffer, const uint8_t dataLength, uint8_t * const flags, uint8_t * const tx) { - UNUSED(p1); UNUSED(p2); UNUSED(flags); - - if ((MIN_DERIVATION_LENGTH * sizeof(uint32_t) > dataLength) || (MAX_DERIVATION_LENGTH * sizeof(uint32_t) < dataLength)) { - G_io_apdu_buffer[(*tx)++] = R_WRONG_SIZE_ERR; - return; - } - - uint8_t derivationParamLengthInBytes = dataLength; - - if (0 != derivationParamLengthInBytes % sizeof(uint32_t)) { - G_io_apdu_buffer[(*tx)++] = R_UNKNOWN_CMD_PARAM_ERR; - return; - } - - G_io_apdu_buffer[(*tx)++] = R_SUCCESS; + UNUSED(p1); uint16_t exception = 0; uint8_t publicKey[32]; os_memset(publicKey, 0, sizeof(publicKey)); - uint8_t ret = ardorKeys(dataBuffer, derivationParamLengthInBytes / sizeof(uint32_t), 0, publicKey, 0, 0, &exception); //derivationParamLengthInBytes should devied by 4, it's checked above + uint8_t ret = burstKeys(p2, 0, publicKey, 0, &exception); if (R_SUCCESS == ret) { os_memset(screenContent, 0, sizeof(screenContent)); @@ -104,16 +90,10 @@ void showAddressHandlerHelper(const uint8_t p1, const uint8_t p2, const uint8_t reedSolomonEncode(publicKeyToId(publicKey), screenContent + strlen(screenContent)); showScreen(); *flags |= IO_ASYNCH_REPLY; - } else if (R_KEY_DERIVATION_EX == ret) { - G_io_apdu_buffer[0] = ret; - G_io_apdu_buffer[1] = exception >> 8; - G_io_apdu_buffer[2] = exception & 0xFF; - *tx = 3; - return; + G_io_apdu_buffer[(*tx)++] = R_SUCCESS; } else { G_io_apdu_buffer[0] = ret; *tx = 1; - return; } } diff --git a/src/signToken.c b/src/signToken.c index e707f9e..d57e532 100644 --- a/src/signToken.c +++ b/src/signToken.c @@ -27,7 +27,7 @@ #include "returnValues.h" #include "config.h" -#include "ardor.h" +#include "burst.h" #define P1_INIT 0 #define P1_MSG_BYTES 1 @@ -63,7 +63,6 @@ void cleanTokenCreationState() { void signTokenMessageHandlerHelper(const uint8_t p1, const uint8_t p2, const uint8_t * const dataBuffer, const uint8_t dataLength, uint8_t * const flags, uint8_t * const tx, const bool isLastCommandDifferent) { - UNUSED(p2); UNUSED(flags); if (isLastCommandDifferent) @@ -113,22 +112,13 @@ void signTokenMessageHandlerHelper(const uint8_t p1, const uint8_t p2, const uin break; } - //underflow was checked against above above - uint8_t derivationPathLengthInUints32 = (dataLength - 4) / sizeof(uint32_t); - - if ((MIN_DERIVATION_LENGTH > derivationPathLengthInUints32) || (MAX_DERIVATION_LENGTH < derivationPathLengthInUints32)) { - cleanTokenCreationState(); - G_io_apdu_buffer[(*tx)++] = R_WRONG_SIZE_ERR; - break; - } - uint16_t exception = 0; uint32_t timestamp = 0; - uint8_t keySeed[32]; os_memset(keySeed, 0, sizeof(keySeed)); + uint8_t sharedKey[32]; os_memset(sharedKey, 0, sizeof(sharedKey)); //gotta do some space reuse uint8_t publicKeyAndFinalHash[32]; os_memset(publicKeyAndFinalHash, 0, sizeof(publicKeyAndFinalHash)); - uint8_t ret = ardorKeys(dataBuffer + sizeof(timestamp), derivationPathLengthInUints32, keySeed, publicKeyAndFinalHash, 0, 0, &exception); //derivationParamLengthInBytes should devied by 4, it's checked above + uint8_t ret = burstKeys(p2, NULL, publicKeyAndFinalHash, sharedKey, &exception); if (R_SUCCESS != ret) { cleanTokenCreationState(); @@ -161,8 +151,8 @@ void signTokenMessageHandlerHelper(const uint8_t p1, const uint8_t p2, const uin cx_hash(&state.tokenCreation.sha256.header, CX_LAST, 0, 0, publicKeyAndFinalHash, sizeof(publicKeyAndFinalHash)); - signMsg(keySeed, publicKeyAndFinalHash, G_io_apdu_buffer + *tx); //is a void function, no ret value to check against - os_memset(keySeed, 0, sizeof(keySeed)); + signMsg(sharedKey, publicKeyAndFinalHash, G_io_apdu_buffer + *tx); //is a void function, no ret value to check against + explicit_bzero(sharedKey, sizeof(sharedKey)); *tx += 64; diff --git a/test.py b/test.py new file mode 100644 index 0000000..6d3e2e7 --- /dev/null +++ b/test.py @@ -0,0 +1,118 @@ +from ledgerblue.comm import getDongle +from ledgerblue.commException import CommException +# from secp256k1 import PublicKey + +import binascii +import codecs + +dongle = getDongle(True) + +CLA = "80" +INDEX = "01" +P1 = "00" +P2 = INDEX +LEN = "00" +DATA = "" + +# Instructions accepted by the Burst Ledger App +INS_GET_VERSION = "01" +INS_AUTH_SIGN_TXN = "03" +INS_ENCRYPT_DECRYPT_MSG = "04" +INS_SHOW_ADDRESS = "05" +INS_GET_PUBLIC_KEY = "06" +INS_SIGN_TOKEN = "07" + +INS = INS_GET_VERSION +ret = dongle.exchange(bytearray.fromhex(CLA + INS + P1 + P2 + LEN + DATA)) +print("version ", binascii.hexlify(ret)) + +INS = INS_GET_PUBLIC_KEY +ret = dongle.exchange(bytearray.fromhex(CLA + INS + P1 + P2 + LEN + DATA)) +print("ret ", str(ret[0])) +print("publicKey ", binascii.hexlify(ret[1:1+32])) + +# Show the address for the given index, blocks for user input (wait for an accept) +# INS = INS_SHOW_ADDRESS +# ret = dongle.exchange(bytearray.fromhex(CLA + INS + P1 + P2 + LEN + DATA)) +# print("ret ", str(ret[0])) + +# An ordinary payment transaction +INS = INS_AUTH_SIGN_TXN +P1 = "01" # sign init +DATA = "0010b40ad80ae803c980cdc2fded5c1d402fc37eb46eee66706574f037469d47da14f9d7df53f834b6592e05e1c7d3a900e1f505000000008096980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005191020085834c122c1c5665" +LEN = "b0" # 176 +P2 = LEN +ret = dongle.exchange(bytearray.fromhex(CLA + INS + P1 + P2 + LEN + DATA)) +print("ret ", str(ret[0])) +P1 = "03" # sign finish +P2 = INDEX +DATA = "" +LEN = "00" +ret = dongle.exchange(bytearray.fromhex(CLA + INS + P1 + P2 + LEN + DATA)) +print("ret ", str(ret[0])) +print("sig ", binascii.hexlify(ret[1:1+64])) + +# Token transfer transaction +INS = INS_AUTH_SIGN_TXN +P1 = "01" # sign init +DATA = "0211223fd80ae80334a7ca8bbded4e3f24c60ecb655f9235ac1b12d97aea698c554df8bf1d950f2db6592e05e1c7d3a900000000000000008096980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a9102008981c4210120df530151a4f5468b89f5faa00f000000000000" +LEN = "c1" # 193 +P2 = LEN +ret = dongle.exchange(bytearray.fromhex(CLA + INS + P1 + P2 + LEN + DATA)) +print("ret ", str(ret[0])) +P1 = "03" # sign finish +P2 = INDEX +DATA = "" +LEN = "00" +ret = dongle.exchange(bytearray.fromhex(CLA + INS + P1 + P2 + LEN + DATA)) +print("ret ", str(ret[0])) +print("sig ", binascii.hexlify(ret[1:1+64])) + + +# Token transfer transaction (TRT) +INS = INS_AUTH_SIGN_TXN +P1 = "01" # sign init +DATA = "0211e405d90aa005416d25901e5b4f8e03d00c92fd508798d3794883e4a73630ab9e88454b7aed49d84228d09c9f7e2d0000000000000000b0dfc90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009a7b0b0025241b746d4b92e2018409fe8f2e3b1eace803000000000000" +LEN = "c1" # 193 +P2 = LEN +ret = dongle.exchange(bytearray.fromhex(CLA + INS + P1 + P2 + LEN + DATA)) +print("ret ", str(ret[0])) +P1 = "03" # sign finish +P2 = INDEX +DATA = "" +LEN = "00" +ret = dongle.exchange(bytearray.fromhex(CLA + INS + P1 + P2 + LEN + DATA)) +print("ret ", str(ret[0])) +print("sig ", binascii.hexlify(ret[1:1+64])) + +# Token buy offer (TRT) +INS = INS_AUTH_SIGN_TXN +P1 = "01" # sign init +DATA = "02130609d90aa005416d25901e5b4f8e03d00c92fd508798d3794883e4a73630ab9e88454b7aed4900000000000000000000000000000000209586000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a27b0b00add44456cab796a4018409fe8f2e3b1eac50c3000000000000c409000000000000" +LEN = "c9" # 201 +P2 = LEN +ret = dongle.exchange(bytearray.fromhex(CLA + INS + P1 + P2 + LEN + DATA)) +print("ret ", str(ret[0])) +P1 = "03" # sign finish +P2 = INDEX +DATA = "" +LEN = "00" +ret = dongle.exchange(bytearray.fromhex(CLA + INS + P1 + P2 + LEN + DATA)) +print("ret ", str(ret[0])) +print("sig ", binascii.hexlify(ret[1:1+64])) + +# Token cancel offer +INS = INS_AUTH_SIGN_TXN +P1 = "01" # sign init +DATA = "0215840cd90aa005416d25901e5b4f8e03d00c92fd508798d3794883e4a73630ab9e88454b7aed4900000000000000000000000000000000209586000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a47b0b0065491ec2c52052c7019567ff4148b62b98" +LEN = "b9" # 185 +P2 = LEN +ret = dongle.exchange(bytearray.fromhex(CLA + INS + P1 + P2 + LEN + DATA)) +print("ret ", str(ret[0])) +P1 = "03" # sign finish +P2 = INDEX +DATA = "" +LEN = "00" +ret = dongle.exchange(bytearray.fromhex(CLA + INS + P1 + P2 + LEN + DATA)) +print("ret ", str(ret[0])) +print("sig ", binascii.hexlify(ret[1:1+64])) \ No newline at end of file diff --git a/txtypes.txt b/txtypes.txt deleted file mode 100644 index 81ef2cd..0000000 --- a/txtypes.txt +++ /dev/null @@ -1,60 +0,0 @@ --4,0,Parent Coin Exchange Order Issue --4,1,Parent Coin Exchange Order Cancel --3,0,Effective Balance Leasing --2,0,Parent Payment --1,0,Child Chain Block -0,0,Ordinary Payment -1,0,Arbitrary Message -2,0,Asset Issuance -2,1,Asset Transfer -2,2,Ask Order Placement -2,3,Bid Order Placement -2,4,Ask Order Cancellation -2,5,Bid Order Cancellation -2,6,Dividend Payment -2,7,Asset Delete -2,8,Asset Increase -2,9,Set Phasing Asset Control -2,10,Asset Property -2,11,Asset Property Delete -3,0,Digital Goods Listing -3,1,Digital Goods Delisting -3,2,Digital Goods Price Change -3,3,Digital Goods Quantity Change -3,4,Digital Goods Purchase -3,5,Digital Goods Delivery -3,6,Digital Goods Feedback -3,7,Digital Goods Refund -4,0,Set Phasing Only -5,0,Currency Issuance -5,1,Reserve Increase -5,2,Reserve Claim -5,3,Currency Transfer -5,4,Publish Exchange Offer -5,5,Exchange Buy -5,6,Exchange Sell -5,7,Currency Minting -5,8,Currency Deletion -6,0,Tagged Data Upload -7,0,Shuffling Creation -7,1,Shuffling Registration -7,2,Shuffling Processing -7,3,Shuffling Recipients -7,4,Shuffling Verification -7,5,Shuffling Cancellation -8,0,Alias Assignment -8,1,Alias Sell -8,2,Alias Buy -8,3,Alias Delete -9,0,Poll Creation -9,1,Vote Casting -9,2,Phasing Vote Casting -10,0,Account Info -10,1,Account Property -10,2,Account Property Delete -11,0,Coin Exchange Order Issue -11,1,Coin Exchange Order Cancel -12,0,Contract Reference -12,1,Contract Reference Delete -13,0,Add Permission -13,1,Remove Permission \ No newline at end of file