Skip to content

Commit

Permalink
Huge refactor with custom transaction and SIGN_TX command
Browse files Browse the repository at this point in the history
  • Loading branch information
grydz committed Nov 26, 2020
1 parent d5f0bc3 commit 7ddd1d0
Show file tree
Hide file tree
Showing 64 changed files with 1,623 additions and 347 deletions.
2 changes: 1 addition & 1 deletion .doxygen/Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ NUM_PROC_THREADS = 1
# normally produced when WARNINGS is set to YES.
# The default value is: NO.

EXTRACT_ALL = YES
EXTRACT_ALL = NO

# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
# be included in the documentation.
Expand Down
16 changes: 12 additions & 4 deletions .github/workflows/ci-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ jobs:
lcov --directory . -b "$(realpath build/)" --remove coverage.info '*/unit-tests/*' -o coverage.info && \
genhtml coverage.info -o coverage
- name: Upload code coverage
uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v2
with:
name: code-coverage
path: unit-tests/coverage
Expand All @@ -65,6 +64,16 @@ jobs:
name: codecov-app-boilerplate
fail_ci_if_error: true
verbose: true

- name: HTML documentation
run: doxygen .doxygen/Doxyfile

- uses: actions/upload-artifact@v2
with:
name: documentation
path: doc/html



job_test:
name: Test
Expand Down Expand Up @@ -95,8 +104,7 @@ jobs:
- name: Run test
run: |
nohup bash -c "python /speculos/speculos.py bin/app.elf --sdk 1.6 --apdu-port 9999 --button-port 42000 --automation-port 43000 --display headless" > speculos.log 2<&1 &
pip install -r tests/requirements.txt
pytest
cd tests && pip install -r requirements.txt && pytest --headless
- name: Upload Speculos log
uses: actions/upload-artifact@v2
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - 2020-11-19

### Added

- Inital commit with the brand new Boilerplate application
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ BOLOS_SDK=/opt/nanos-secure-sdk

```
make DEBUG=1 # compile optionally with PRINTF (require debug firmware)
make load # load the app on the Nano using ledgerblue
make load # load the app on the Nano using ledgerblue
```

## Continuous integration
## Tests & Continuous Integration

The flow processed in [GitHub Actions](https://github.com/features/actions) is the following:

Expand All @@ -41,3 +41,4 @@ It outputs 4 artifacts:
- `boilerplate-app-debug` within output files of the compilation process in debug mode
- `speculos-log` within APDU commands and response when executing end-to-end tests
- `code-coverage` within HTML details of code coverage
- `documentation` within HTML auto-generated documentation
44 changes: 44 additions & 0 deletions src/address.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*****************************************************************************
* Ledger App Boilerplate.
* (c) 2020 Ledger SAS.
*
* 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 <stdint.h> // uint*_t
#include <stddef.h> // size_t
#include <stdbool.h> // bool
#include <string.h> // memmove

#include "os.h"
#include "cx.h"

#include "address.h"

#include "transaction/types.h"

bool address_from_pubkey(uint8_t public_key[static 64], uint8_t *out, size_t out_len) {
uint8_t address[32] = {0};
cx_sha3_t sha3;

if (out_len < ADDRESS_LEN) {
return false;
}

cx_keccak_init(&sha3, 256);
cx_hash((cx_hash_t *) &sha3, CX_LAST, public_key, 64, address, sizeof(address));

memmove(out, address + sizeof(address) - ADDRESS_LEN, ADDRESS_LEN);

return true;
}
7 changes: 7 additions & 0 deletions src/address.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once

#include <stdint.h> // uint*_t
#include <stddef.h> // size_t
#include <stdbool.h> // bool

bool address_from_pubkey(uint8_t public_key[static 64], uint8_t *out, size_t out_len);
33 changes: 30 additions & 3 deletions src/apdu/dispatcher.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,40 +19,67 @@
#include <stdbool.h>

#include "dispatcher.h"
#include "../constants.h"
#include "../globals.h"
#include "../types.h"
#include "../io.h"
#include "../sw.h"
#include "../common/buffer.h"
#include "../handler/get_version.h"
#include "../handler/get_app_name.h"
#include "../handler/get_public_key.h"
#include "../handler/sign_tx.h"

int apdu_dispatcher(const command_t *cmd) {
if (cmd->cla != CLA) {
return io_send_sw(SW_CLA_NOT_SUPPORTED);
}

buffer_t buf = {0};

switch (cmd->ins) {
case GET_VERSION:
if (cmd->p1 != 0 || cmd->p2 != 0) {
return io_send_sw(SW_WRONG_P1P2);
}

return handler_get_version();
case GET_APP_NAME:
if (cmd->p1 != 0 || cmd->p2 != 0) {
return io_send_sw(SW_WRONG_P1P2);
}

return handler_get_app_name();
case GET_PUBLIC_KEY: {
case GET_PUBLIC_KEY:
if (cmd->p1 > 1 || cmd->p2 > 0) {
return io_send_sw(SW_WRONG_P1P2);
}

if (!cmd->data) {
return io_send_sw(SW_WRONG_DATA_LENGTH);
}
buffer_t buf = {.ptr = cmd->data, .size = cmd->lc, .offset = 0};

buf.ptr = cmd->data;
buf.size = cmd->lc;
buf.offset = 0;

return handler_get_public_key(&buf, (bool) cmd->p1);
}
case SIGN_TX:
if ((cmd->p1 == P1_START && cmd->p2 != P2_MORE) || //
cmd->p1 > P1_MAX || //
(cmd->p2 != P2_LAST && cmd->p2 != P2_MORE)) {
return io_send_sw(SW_WRONG_P1P2);
}

if (!cmd->data) {
return io_send_sw(SW_WRONG_DATA_LENGTH);
}

buf.ptr = cmd->data;
buf.size = cmd->lc;
buf.offset = 0;

return handler_sign_tx(&buf, cmd->p1, (bool) (cmd->p2 & P2_MORE));
default:
return io_send_sw(SW_INS_NOT_SUPPORTED);
}
Expand Down
5 changes: 5 additions & 0 deletions src/apdu/dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

#include "../types.h"

#define P2_LAST 0x00
#define P2_MORE 0x80
#define P1_START 0x00
#define P1_MAX 0x03

/**
* Function to dispatch APDU command.
*
Expand Down
2 changes: 1 addition & 1 deletion src/apdu/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ bool apdu_parser(command_t *cmd, uint8_t *buf, size_t buf_len) {
}

cmd->cla = buf[OFFSET_CLA];
cmd->ins = buf[OFFSET_INS];
cmd->ins = (command_e) buf[OFFSET_INS];
cmd->p1 = buf[OFFSET_P1];
cmd->p2 = buf[OFFSET_P2];
cmd->lc = buf[OFFSET_LC];
Expand Down
30 changes: 15 additions & 15 deletions src/common/base58.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@
#include "base58.h"

uint8_t const BASE58_TABLE[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //
0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xff, 0xff, //
0xff, 0xff, 0xff, 0xff, 0xff, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, //
0x10, 0xff, 0x11, 0x12, 0x13, 0x14, 0x15, 0xff, 0x16, 0x17, 0x18, 0x19, //
0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, //
0xff, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, //
0xff, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, //
0x37, 0x38, 0x39, 0xff, 0xff, 0xff, 0xff, 0xff //
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //
0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xFF, 0xFF, //
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, //
0x10, 0xFF, 0x11, 0x12, 0x13, 0x14, 0x15, 0xFF, 0x16, 0x17, 0x18, 0x19, //
0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //
0xFF, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, //
0xFF, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, //
0x37, 0x38, 0x39, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF //
};

uint8_t const BASE58_ALPHABET[] = {
char const BASE58_ALPHABET[] = {
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', //
'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', //
'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', //
Expand All @@ -62,7 +62,7 @@ int base58_decode(const char *in, size_t in_len, uint8_t *out, size_t out_len) {

tmp[i] = BASE58_TABLE[(int) in[i]];

if (tmp[i] == 0xff) {
if (tmp[i] == 0xFF) {
return -1;
}
}
Expand All @@ -76,7 +76,7 @@ int base58_decode(const char *in, size_t in_len, uint8_t *out, size_t out_len) {
while (start_at < in_len) {
uint16_t remainder = 0;
for (uint8_t div_loop = start_at; div_loop < in_len; div_loop++) {
uint16_t digit256 = (uint16_t)(tmp[div_loop] & 0xff);
uint16_t digit256 = (uint16_t)(tmp[div_loop] & 0xFF);
uint16_t tmp_div = remainder * 58 + digit256;
tmp[div_loop] = (uint8_t)(tmp_div / 256);
remainder = tmp_div % 256;
Expand Down Expand Up @@ -104,7 +104,7 @@ int base58_decode(const char *in, size_t in_len, uint8_t *out, size_t out_len) {
return length;
}

int base58_encode(const uint8_t *in, size_t in_len, uint8_t *out, size_t out_len) {
int base58_encode(const uint8_t *in, size_t in_len, char *out, size_t out_len) {
uint8_t buffer[MAX_ENC_INPUT_SIZE * 138 / 100 + 1] = {0};
size_t i, j;
size_t stop_at;
Expand Down
8 changes: 4 additions & 4 deletions src/common/base58.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
* @see https://tools.ietf.org/html/draft-msporny-base58-02
*
* @param[in] in pointer to string buffer to be decoded.
* @param[in] in pointer to input string buffer to be decoded.
* @param[in] in_len length of the string buffer.
* @param[out] out pointer to array.
* @param[in] out_len maximum length to be stored in out.
Expand All @@ -35,10 +35,10 @@ int base58_decode(const char *in, size_t in_len, uint8_t *out, size_t out_len);
*
* @see https://tools.ietf.org/html/draft-msporny-base58-02
*
* @param[in] in pointer to byte buffer to be encoded.
* @param[in] in pointer to input byte buffer to be encoded.
* @param[in] in_len length of the byte buffer.
* @param[out] out pointer to byte buffer.
* @param[out] out pointer to output string buffer.
* @param[in] out_len maximum length to be stored in out.
*
*/
int base58_encode(const uint8_t *in, size_t in_len, uint8_t *out, size_t out_len);
int base58_encode(const uint8_t *in, size_t in_len, char *out, size_t out_len);
14 changes: 8 additions & 6 deletions src/common/bip32.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,30 @@
#include <stdbool.h> // bool

#include "bip32.h"
#include "buffer.h"
#include "read.h"

bool bip32_path_from_buffer(buffer_t *buf, uint32_t *out, size_t out_len) {
bool bip32_path_read(const uint8_t *in, size_t in_len, uint32_t *out, size_t out_len) {
if (out_len == 0 || out_len > MAX_BIP32_PATH) {
return false;
}

size_t offset = 0;

for (size_t i = 0; i < out_len; i++) {
if (!buffer_read_u32(buf, out + i, BE)) {
if (offset > in_len) {
return false;
}
out[i] = read_u32_be(in, offset);
offset += 4;
}

return true;
}

bool bip32_path_to_str(const uint32_t *bip32_path,
bool bip32_path_format(const uint32_t *bip32_path,
size_t bip32_path_len,
char *out,
size_t out_len) {
memset(out, 0, out_len);

if (bip32_path_len == 0 || bip32_path_len > MAX_BIP32_PATH) {
return false;
}
Expand Down
19 changes: 9 additions & 10 deletions src/common/bip32.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,28 @@
#include <stdint.h> // uint*_t
#include <stdbool.h> // bool

#include "buffer.h"

/**
* Maximum length of BIP32 path allowed.
*/
#define MAX_BIP32_PATH 10

/**
* Function to convert BIP32 path bytes to array of integers.
* Function to read BIP32 path.
*
* @brief read bytes of BIP32 path in buffer and convert to array of 32 bit integers.
* @brief read BIP32 path in byte array.
*
* @param[in,out] buf buffer with bytes of BIP32 path.
* @param[out] out pointer to array of 32 bit integers.
* @param[in] out_len lenght of the array pointed.
* @param[in] in pointer to byte array.
* @param[in] in_len length of byte array.
* @param[out] out pointer to 32-bit integer array.
* @param[in] out_len lenght of the 32-bit integer array.
*
* @return true if success, false otherwise.
*
*/
bool bip32_path_from_buffer(buffer_t *buf, uint32_t *out, size_t out_len);
bool bip32_path_read(const uint8_t *in, size_t in_len, uint32_t *out, size_t out_len);

/**
* Function to convert BIP32 path to string.
* Function to format BIP32 path as string.
*
* @brief convert 32 bit integer array with BIP32 path to string.
*
Expand All @@ -38,7 +37,7 @@ bool bip32_path_from_buffer(buffer_t *buf, uint32_t *out, size_t out_len);
* @return true if success, false otherwise.
*
*/
bool bip32_path_to_str(const uint32_t *bip32_path,
bool bip32_path_format(const uint32_t *bip32_path,
size_t bip32_path_len,
char *out,
size_t out_len);
Loading

0 comments on commit 7ddd1d0

Please sign in to comment.