From be17ddabb10e6265e0e42cd38b625104bc9519f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Novo?= <34069419+TeknoPT@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:33:33 +0000 Subject: [PATCH] Update dependencies. - Update app_main - Update Common depencencies - Update send_response to feature phantasma. --- src/app_main.c | 3 + src/common/base58.c | 155 +++++++++++++++++++++++++++ src/common/base58.h | 52 +++++++++ src/common/bip32.c | 87 +++++++++++++++ src/common/bip32.h | 47 ++++++++ src/common/buffer.c | 185 ++++++++++++++++++++++++++++++++ src/common/buffer.h | 220 ++++++++++++++++++++++++++++++++++++++ src/common/format.c | 157 +++++++++++++++++++++++++++ src/common/format.h | 69 ++++++++++++ src/common/macros.h | 6 ++ src/common/read.c | 64 +++++++++++ src/common/read.h | 82 ++++++++++++++ src/common/varint.c | 101 +++++++++++++++++ src/common/varint.h | 52 +++++++++ src/common/write.c | 64 +++++++++++ src/common/write.h | 82 ++++++++++++++ src/helper/send_reponse.c | 18 +++- 17 files changed, 1439 insertions(+), 5 deletions(-) create mode 100644 src/common/base58.c create mode 100644 src/common/base58.h create mode 100644 src/common/bip32.c create mode 100644 src/common/bip32.h create mode 100644 src/common/buffer.c create mode 100644 src/common/buffer.h create mode 100644 src/common/format.c create mode 100644 src/common/format.h create mode 100644 src/common/macros.h create mode 100644 src/common/read.c create mode 100644 src/common/read.h create mode 100644 src/common/varint.c create mode 100644 src/common/varint.h create mode 100644 src/common/write.c create mode 100644 src/common/write.h diff --git a/src/app_main.c b/src/app_main.c index 30e877c..23769a4 100644 --- a/src/app_main.c +++ b/src/app_main.c @@ -58,6 +58,9 @@ void app_main() { } for (;;) { + // Reset structured APDU command + memset(&cmd, 0, sizeof(cmd)); + // Receive command bytes in G_io_apdu_buffer if ((input_len = io_recv_command()) < 0) { PRINTF("=> io_recv_command failure\n"); diff --git a/src/common/base58.c b/src/common/base58.c new file mode 100644 index 0000000..4c15dbd --- /dev/null +++ b/src/common/base58.c @@ -0,0 +1,155 @@ +/***************************************************************************** + * (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 // size_t +#include // uint*_t +#include // memmove, memset +#include // bool + +#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 // +}; + +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', // + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' // +}; + +int base58_decode(const char *in, size_t in_len, uint8_t *out, size_t out_len) { + uint8_t tmp[MAX_DEC_INPUT_SIZE] = {0}; + uint8_t buffer[MAX_DEC_INPUT_SIZE] = {0}; + uint8_t j; + uint8_t start_at; + uint8_t zero_count = 0; + + if (in_len > MAX_DEC_INPUT_SIZE || in_len < 2) { + return -1; + } + + memmove(tmp, in, in_len); + + for (uint8_t i = 0; i < in_len; i++) { + if (in[i] >= sizeof(BASE58_TABLE)) { + return -1; + } + + tmp[i] = BASE58_TABLE[(int) in[i]]; + + if (tmp[i] == 0xFF) { + return -1; + } + } + + while ((zero_count < in_len) && (tmp[zero_count] == 0)) { + ++zero_count; + } + + j = in_len; + start_at = zero_count; + 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 tmp_div = remainder * 58 + digit256; + tmp[div_loop] = (uint8_t)(tmp_div / 256); + remainder = tmp_div % 256; + } + + if (tmp[start_at] == 0) { + ++start_at; + } + + buffer[--j] = (uint8_t) remainder; + } + + while ((j < in_len) && (buffer[j] == 0)) { + ++j; + } + + int length = in_len - (j - zero_count); + + if ((int) out_len < length) { + return -1; + } + + memmove(out, buffer + j - zero_count, length); + + return length; +} + +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; + size_t zero_count = 0; + size_t output_size; + + if (in_len > MAX_ENC_INPUT_SIZE) { + return -1; + } + + while ((zero_count < in_len) && (in[zero_count] == 0)) { + ++zero_count; + } + + output_size = (in_len - zero_count) * 138 / 100 + 1; + stop_at = output_size - 1; + for (size_t start_at = zero_count; start_at < in_len; start_at++) { + int carry = in[start_at]; + for (j = output_size - 1; (int) j >= 0; j--) { + carry += 256 * buffer[j]; + buffer[j] = carry % 58; + carry /= 58; + + if (j <= stop_at - 1 && carry == 0) { + break; + } + } + stop_at = j; + } + + j = 0; + while (j < output_size && buffer[j] == 0) { + j += 1; + } + + if (out_len < zero_count + output_size - j) { + return -1; + } + + memset(out, BASE58_ALPHABET[0], zero_count); + + i = zero_count; + while (j < output_size) { + out[i++] = BASE58_ALPHABET[buffer[j++]]; + } + + return i; +} diff --git a/src/common/base58.h b/src/common/base58.h new file mode 100644 index 0000000..f214afd --- /dev/null +++ b/src/common/base58.h @@ -0,0 +1,52 @@ +#pragma once + +#include // size_t +#include // uint*_t +#include // bool + +/** + * Maximum length of input when decoding in base 58. + */ +#define MAX_DEC_INPUT_SIZE 164 +/** + * Maximum length of input when encoding in base 58. + */ +#define MAX_ENC_INPUT_SIZE 120 + +/** + * Decode input string in base 58. + * + * @see https://tools.ietf.org/html/draft-msporny-base58-02 + * + * @param[in] in + * Pointer to input string buffer. + * @param[in] in_len + * Length of the input string buffer. + * @param[out] out + * Pointer to output byte buffer. + * @param[in] out_len + * Maximum length to write in output byte buffer. + * + * @return number of bytes decoded, -1 otherwise. + * + */ +int base58_decode(const char *in, size_t in_len, uint8_t *out, size_t out_len); + +/** + * Encode input bytes in base 58. + * + * @see https://tools.ietf.org/html/draft-msporny-base58-02 + * + * @param[in] in + * Pointer to input byte buffer. + * @param[in] in_len + * Length of the input byte buffer. + * @param[out] out + * Pointer to output string buffer. + * @param[in] out_len + * Maximum length to write in output byte buffer. + * + * @return number of bytes encoded, -1 otherwise. + * + */ +int base58_encode(const uint8_t *in, size_t in_len, char *out, size_t out_len); diff --git a/src/common/bip32.c b/src/common/bip32.c new file mode 100644 index 0000000..a6ac6e5 --- /dev/null +++ b/src/common/bip32.c @@ -0,0 +1,87 @@ +/***************************************************************************** + * (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 // snprintf +#include // memset, strlen +#include // size_t +#include // uint*_t +#include // bool + +#include "bip32.h" +#include "read.h" + +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 (offset > in_len) { + return false; + } + out[i] = read_u32_be(in, offset); + offset += 4; + } + + return true; +} + +bool bip32_path_format(const uint32_t *bip32_path, + size_t bip32_path_len, + char *out, + size_t out_len) { + if (bip32_path_len == 0 || bip32_path_len > MAX_BIP32_PATH) { + return false; + } + + size_t offset = 0; + + for (uint16_t i = 0; i < bip32_path_len; i++) { + size_t written; + + snprintf(out + offset, out_len - offset, "%d", bip32_path[i] & 0x7FFFFFFFu); + written = strlen(out + offset); + if (written == 0 || written >= out_len - offset) { + memset(out, 0, out_len); + return false; + } + offset += written; + + if ((bip32_path[i] & 0x80000000u) != 0) { + snprintf(out + offset, out_len - offset, "'"); + written = strlen(out + offset); + if (written == 0 || written >= out_len - offset) { + memset(out, 0, out_len); + return false; + } + offset += written; + } + + if (i != bip32_path_len - 1) { + snprintf(out + offset, out_len - offset, "/"); + written = strlen(out + offset); + if (written == 0 || written >= out_len - offset) { + memset(out, 0, out_len); + return false; + } + offset += written; + } + } + + return true; +} diff --git a/src/common/bip32.h b/src/common/bip32.h new file mode 100644 index 0000000..3e2491e --- /dev/null +++ b/src/common/bip32.h @@ -0,0 +1,47 @@ +#pragma once + +#include // size_t +#include // uint*_t +#include // bool + +/** + * Maximum length of BIP32 path allowed. + */ +#define MAX_BIP32_PATH 10 + +/** + * Read BIP32 path from byte buffer. + * + * @param[in] in + * Pointer to input byte buffer. + * @param[in] in_len + * Length of input byte buffer. + * @param[out] out + * Pointer to output 32-bit integer buffer. + * @param[in] out_len + * Number of BIP32 paths read in the output buffer. + * + * @return true if success, false otherwise. + * + */ +bool bip32_path_read(const uint8_t *in, size_t in_len, uint32_t *out, size_t out_len); + +/** + * Format BIP32 path as string. + * + * @param[in] bip32_path + * Pointer to 32-bit integer input buffer. + * @param[in] bip32_path_len + * Maximum number of BIP32 paths in the input buffer. + * @param[out] out string + * Pointer to output string. + * @param[in] out_len + * Length of the output string. + * + * @return true if success, false otherwise. + * + */ +bool bip32_path_format(const uint32_t *bip32_path, + size_t bip32_path_len, + char *out, + size_t out_len); diff --git a/src/common/buffer.c b/src/common/buffer.c new file mode 100644 index 0000000..ab8f40e --- /dev/null +++ b/src/common/buffer.c @@ -0,0 +1,185 @@ +/***************************************************************************** + * (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 // uint*_t +#include // size_t +#include // bool +#include // memmove + +#include "buffer.h" +#include "read.h" +#include "varint.h" +#include "bip32.h" + +bool buffer_can_read(const buffer_t *buffer, size_t n) { + return buffer->size - buffer->offset >= n; +} + +bool buffer_seek_set(buffer_t *buffer, size_t offset) { + if (offset > buffer->size) { + return false; + } + + buffer->offset = offset; + + return true; +} + +bool buffer_seek_cur(buffer_t *buffer, size_t offset) { + if (buffer->offset + offset < buffer->offset || // overflow + buffer->offset + offset > buffer->size) { // exceed buffer size + return false; + } + + buffer->offset += offset; + + return true; +} + +bool buffer_seek_end(buffer_t *buffer, size_t offset) { + if (offset > buffer->size) { + return false; + } + + buffer->offset = buffer->size - offset; + + return true; +} + +bool buffer_read_u8(buffer_t *buffer, uint8_t *value) { + if (!buffer_can_read(buffer, 1)) { + *value = 0; + + return false; + } + + *value = buffer->ptr[buffer->offset]; + buffer_seek_cur(buffer, 1); + + return true; +} + +bool buffer_read_u16(buffer_t *buffer, uint16_t *value, endianness_t endianness) { + if (!buffer_can_read(buffer, 2)) { + *value = 0; + + return false; + } + + *value = ((endianness == BE) ? read_u16_be(buffer->ptr, buffer->offset) + : read_u16_le(buffer->ptr, buffer->offset)); + + buffer_seek_cur(buffer, 2); + + return true; +} + +bool buffer_read_u32(buffer_t *buffer, uint32_t *value, endianness_t endianness) { + if (!buffer_can_read(buffer, 4)) { + *value = 0; + + return false; + } + + *value = ((endianness == BE) ? read_u32_be(buffer->ptr, buffer->offset) + : read_u32_le(buffer->ptr, buffer->offset)); + + buffer_seek_cur(buffer, 4); + + return true; +} + +bool buffer_read_u64(buffer_t *buffer, uint64_t *value, endianness_t endianness) { + if (!buffer_can_read(buffer, 8)) { + *value = 0; + + return false; + } + + *value = ((endianness == BE) ? read_u64_be(buffer->ptr, buffer->offset) + : read_u64_le(buffer->ptr, buffer->offset)); + + buffer_seek_cur(buffer, 8); + + return true; +} + +bool buffer_read_varint(buffer_t *buffer, uint64_t *value) { + int length = varint_read(buffer->ptr + buffer->offset, buffer->size - buffer->offset, value); + + if (length < 0) { + *value = 0; + + return false; + } + + buffer_seek_cur(buffer, (size_t) length); + + return true; +} + +bool buffer_read_bip32_path(buffer_t *buffer, uint32_t *out, size_t out_len) { + if (!bip32_path_read(buffer->ptr + buffer->offset, + buffer->size - buffer->offset, + out, + out_len)) { + return false; + } + + buffer_seek_cur(buffer, sizeof(*out) * out_len); + + return true; +} + +bool buffer_copy(const buffer_t *buffer, uint8_t *out, size_t out_len) { + if (buffer->size - buffer->offset > out_len) { + return false; + } + + memmove(out, buffer->ptr + buffer->offset, buffer->size - buffer->offset); + + return true; +} + +bool buffer_move(buffer_t *buffer, uint8_t *out, size_t out_len) { + if (!buffer_copy(buffer, out, out_len)) { + return false; + } + + buffer_seek_cur(buffer, out_len); + + return true; +} + +bool to_hex(const char ch0, const char ch1, uint8_t *out) { + uint8_t nib0 = ((ch0 & 0xF) + (ch0 >> 6)) | ((ch0 >> 3) & 0x8); + uint8_t nib1 = ((ch1 & 0xF) + (ch1 >> 6)) | ((ch1 >> 3) & 0x8); + uint8_t c = (nib0 << 4) | nib1; + *out = c; + return true; +} + +bool buffer_read_hex(buffer_t *script_buf, uint8_t *out) { + uint8_t ch0; + uint8_t ch1; + if (!buffer_read_u8(script_buf, &ch0)) { + return false; + } + if (!buffer_read_u8(script_buf, &ch1)) { + return false; + } + return to_hex(ch0, ch1, out); +} diff --git a/src/common/buffer.h b/src/common/buffer.h new file mode 100644 index 0000000..d392ea2 --- /dev/null +++ b/src/common/buffer.h @@ -0,0 +1,220 @@ +#pragma once + +#include // uint*_t +#include // size_t +#include // bool + +/** + * Enumeration for endianness. + */ +typedef enum { + BE, /// Big Endian + LE /// Little Endian +} endianness_t; + +/** + * Struct for buffer with size and offset. + */ +typedef struct { + const uint8_t *ptr; /// Pointer to byte buffer + size_t size; /// Size of byte buffer + size_t offset; /// Offset in byte buffer +} buffer_t; + +/** + * Tell whether buffer can read bytes or not. + * + * @param[in] buffer + * Pointer to input buffer struct. + * @param[in] n + * Number of bytes to read in buffer. + * + * @return true if success, false otherwise. + * + */ +bool buffer_can_read(const buffer_t *buffer, size_t n); + +/** + * Seek the buffer to specific offset. + * + * @param[in,out] buffer + * Pointer to input buffer struct. + * @param[in] offset + * Specific offset to seek. + * + * @return true if success, false otherwise. + * + */ +bool buffer_seek_set(buffer_t *buffer, size_t offset); + +/** + * Seek buffer relatively to current offset. + * + * @param[in,out] buffer + * Pointer to input buffer struct. + * @param[in] offset + * Offset to seek relatively to `buffer->offset`. + * + * @return true if success, false otherwise. + * + */ +bool buffer_seek_cur(buffer_t *buffer, size_t offset); + +/** + * Seek the buffer relatively to the end. + * + * @param[in,out] buffer + * Pointer to input buffer struct. + * @param[in] offset + * Offset to seek relatively to `buffer->size`. + * + * @return true if success, false otherwise. + * + */ +bool buffer_seek_end(buffer_t *buffer, size_t offset); + +/** + * Read 1 byte from buffer into uint8_t. + * + * @param[in,out] buffer + * Pointer to input buffer struct. + * @param[out] value + * Pointer to 8-bit unsigned integer read from buffer. + * + * @return true if success, false otherwise. + * + */ +bool buffer_read_u8(buffer_t *buffer, uint8_t *value); + +/** + * Read 2 bytes from buffer into uint16_t. + * + * @param[in,out] buffer + * Pointer to input buffer struct. + * @param[out] value + * Pointer to 16-bit unsigned integer read from buffer. + * @param[in] endianness + * Either BE (Big Endian) or LE (Little Endian). + * + * @return true if success, false otherwise. + * + */ +bool buffer_read_u16(buffer_t *buffer, uint16_t *value, endianness_t endianness); + +/** + * Read 4 bytes from buffer into uint32_t. + * + * @param[in,out] buffer + * Pointer to input buffer struct. + * @param[out] value + * Pointer to 32-bit unsigned integer read from buffer. + * @param[in] endianness + * Either BE (Big Endian) or LE (Little Endian). + * + * @return true if success, false otherwise. + * + */ +bool buffer_read_u32(buffer_t *buffer, uint32_t *value, endianness_t endianness); + +/** + * Read 8 bytes from buffer into uint64_t. + * + * @param[in,out] buffer + * Pointer to input buffer struct. + * @param[out] value + * Pointer to 64-bit unsigned integer read from buffer. + * @param[in] endianness + * Either BE (Big Endian) or LE (Little Endian). + * + * @return true if success, false otherwise. + * + */ +bool buffer_read_u64(buffer_t *buffer, uint64_t *value, endianness_t endianness); + +/** + * Read Bitcoin-like varint from buffer into uint64_t. + * + * @see https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer + * + * @param[in,out] buffer + * Pointer to input buffer struct. + * @param[out] value + * Pointer to 64-bit unsigned integer read from buffer. + * + * @return true if success, false otherwise. + * + */ +bool buffer_read_varint(buffer_t *buffer, uint64_t *value); + +/** + * Read BIP32 path from buffer. + * + * @param[in,out] buffer + * Pointer to input buffer struct. + * @param[out] out + * Pointer to output 32-bit integer buffer. + * @param[in] out_len + * Number of BIP32 paths read in the output buffer. + * + * @return true if success, false otherwise. + * + */ +bool buffer_read_bip32_path(buffer_t *buffer, uint32_t *out, size_t out_len); + +/** + * Copy bytes from buffer without moving offset. + * + * @param[in] buffer + * Pointer to input buffer struct. + * @param[out] out + * Pointer to output byte buffer. + * @param[in] out_len + * Length of output byte buffer. + * + * @return true if success, false otherwise. + * + */ +bool buffer_copy(const buffer_t *buffer, uint8_t *out, size_t out_len); + +/** + * Move bytes from buffer. + * + * @param[in,out] buffer + * Pointer to input buffer struct. + * @param[out] out + * Pointer to output byte buffer. + * @param[in] out_len + * Length of output byte buffer. + * + * @return true if success, false otherwise. + * + */ +bool buffer_move(buffer_t *buffer, uint8_t *out, size_t out_len); + +/** + * convert two bytes as hex. + * + * @param[in] ch0 + * Pointer to input char 0. + * @param[in] ch1 + * Pointer to input char 1. + * @param[out] value + * Pointer to 8-bit unsigned integer output. + * + * @return true if success, false otherwise. + * + */ +bool to_hex(const char ch0, char ch1, uint8_t *out); + +/** + * read two bytes from buffer as hex. + * + * @param[in,out] buffer + * Pointer to input buffer struct. + * @param[out] value + * Pointer to 8-bit unsigned integer read from buffer. + * + * @return true if success, false otherwise. + * + */ +bool buffer_read_hex(buffer_t *buffer, uint8_t *out); diff --git a/src/common/format.c b/src/common/format.c new file mode 100644 index 0000000..d616c29 --- /dev/null +++ b/src/common/format.c @@ -0,0 +1,157 @@ +/***************************************************************************** + * (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 // size_t +#include // int*_t, uint*_t +#include // strncpy, memmove +#include // bool + +#include "format.h" + +bool format_i64(char *dst, size_t dst_len, const int64_t value) { + char temp[] = "-9223372036854775808"; + + char *ptr = temp; + int64_t num = value; + int sign = 1; + + if (value < 0) { + sign = -1; + } + + while (num != 0) { + *ptr++ = '0' + (num % 10) * sign; + num /= 10; + } + + if (value < 0) { + *ptr++ = '-'; + } else if (value == 0) { + *ptr++ = '0'; + } + + int distance = (ptr - temp) + 1; + + if ((int) dst_len < distance) { + return false; + } + + size_t index = 0; + + while (--ptr >= temp) { + dst[index++] = *ptr; + } + + dst[index] = '\0'; + + return true; +} + +bool format_u64(char *out, size_t outLen, uint64_t in) { + uint8_t i = 0; + + if (outLen == 0) { + return false; + } + outLen--; + + while (in > 9) { + out[i] = in % 10 + '0'; + in /= 10; + i++; + if (i + 1 > outLen) { + return false; + } + } + out[i] = in + '0'; + out[i + 1] = '\0'; + + uint8_t j = 0; + char tmp; + + // revert the string + while (j < i) { + // swap out[j] and out[i] + tmp = out[j]; + out[j] = out[i]; + out[i] = tmp; + + i--; + j++; + } + return true; +} + +bool format_fpu64(char *dst, size_t dst_len, const uint64_t value, uint8_t decimals) { + char buffer[21] = {0}; + + if (!format_u64(buffer, sizeof(buffer), value)) { + return false; + } + + size_t digits = strlen(buffer); + + if (digits <= decimals) { + if (dst_len <= 2 + decimals - digits) { + return false; + } + *dst++ = '0'; + *dst++ = '.'; + for (uint16_t i = 0; i < decimals - digits; i++, dst++) { + *dst = '0'; + } + dst_len -= 2 + decimals - digits; + strncpy(dst, buffer, dst_len); + } else { + if (dst_len <= digits + 1 + decimals) { + return false; + } + + const size_t shift = digits - decimals; + memmove(dst, buffer, shift); + dst[shift] = '.'; + strncpy(dst + shift + 1, buffer + shift, decimals); + } + + return true; +} + +int format_hex(const uint8_t *in, size_t in_len, char *out, size_t out_len) { + if (out_len < 2 * in_len + 1) { + return -1; + } + + const char hex[] = "0123456789ABCDEF"; + size_t i = 0; + int written = 0; + + while (i < in_len && (i * 2 + (2 + 1)) <= out_len) { + uint8_t high_nibble = (in[i] & 0xF0) >> 4; + *out = hex[high_nibble]; + out++; + + uint8_t low_nibble = in[i] & 0x0F; + *out = hex[low_nibble]; + out++; + + i++; + written += 2; + } + + *out = '\0'; + + return written + 1; +} diff --git a/src/common/format.h b/src/common/format.h new file mode 100644 index 0000000..f145748 --- /dev/null +++ b/src/common/format.h @@ -0,0 +1,69 @@ +#pragma once + +#include // size_t +#include // int*_t, uint*_t +#include // bool + +/** + * Format 64-bit signed integer as string. + * + * @param[out] dst + * Pointer to output string. + * @param[in] dst_len + * Length of output string. + * @param[in] value + * 64-bit signed integer to format. + * + * @return true if success, false otherwise. + * + */ +bool format_i64(char *dst, size_t dst_len, const int64_t value); + +/** + * Format 64-bit unsigned integer as string. + * + * @param[out] dst + * Pointer to output string. + * @param[in] dst_len + * Length of output string. + * @param[in] value + * 64-bit unsigned integer to format. + * + * @return true if success, false otherwise. + * + */ +bool format_u64(char *dst, size_t dst_len, uint64_t value); + +/** + * Format 64-bit unsigned integer as string with decimals. + * + * @param[out] dst + * Pointer to output string. + * @param[in] dst_len + * Length of output string. + * @param[in] value + * 64-bit unsigned integer to format. + * @param[in] decimals + * Number of digits after decimal separator. + * + * @return true if success, false otherwise. + * + */ +bool format_fpu64(char *dst, size_t dst_len, const uint64_t value, uint8_t decimals); + +/** + * Format byte buffer to uppercase hexadecimal string. + * + * @param[in] in + * Pointer to input byte buffer. + * @param[in] in_len + * Length of input byte buffer. + * @param[out] out + * Pointer to output string. + * @param[in] out_len + * Length of output string. + * + * @return number of bytes written if success, -1 otherwise. + * + */ +int format_hex(const uint8_t *in, size_t in_len, char *out, size_t out_len); diff --git a/src/common/macros.h b/src/common/macros.h new file mode 100644 index 0000000..13f75a3 --- /dev/null +++ b/src/common/macros.h @@ -0,0 +1,6 @@ +#pragma once + +/** + * Macro for the size of a specific structure field. + */ +#define MEMBER_SIZE(type, member) (sizeof(((type *) 0)->member)) diff --git a/src/common/read.c b/src/common/read.c new file mode 100644 index 0000000..c8ee851 --- /dev/null +++ b/src/common/read.c @@ -0,0 +1,64 @@ +/***************************************************************************** + * (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 // uint*_t +#include // size_t + +uint16_t read_u16_be(const uint8_t *ptr, size_t offset) { + return (uint16_t) ptr[offset + 0] << 8 | // + (uint16_t) ptr[offset + 1] << 0; +} + +uint32_t read_u32_be(const uint8_t *ptr, size_t offset) { + return (uint32_t) ptr[offset + 0] << 24 | // + (uint32_t) ptr[offset + 1] << 16 | // + (uint32_t) ptr[offset + 2] << 8 | // + (uint32_t) ptr[offset + 3] << 0; +} + +uint64_t read_u64_be(const uint8_t *ptr, size_t offset) { + return (uint64_t) ptr[offset + 0] << 56 | // + (uint64_t) ptr[offset + 1] << 48 | // + (uint64_t) ptr[offset + 2] << 40 | // + (uint64_t) ptr[offset + 3] << 32 | // + (uint64_t) ptr[offset + 4] << 24 | // + (uint64_t) ptr[offset + 5] << 16 | // + (uint64_t) ptr[offset + 6] << 8 | // + (uint64_t) ptr[offset + 7] << 0; +} + +uint16_t read_u16_le(const uint8_t *ptr, size_t offset) { + return (uint16_t) ptr[offset + 0] << 0 | // + (uint16_t) ptr[offset + 1] << 8; +} + +uint32_t read_u32_le(const uint8_t *ptr, size_t offset) { + return (uint32_t) ptr[offset + 0] << 0 | // + (uint32_t) ptr[offset + 1] << 8 | // + (uint32_t) ptr[offset + 2] << 16 | // + (uint32_t) ptr[offset + 3] << 24; +} + +uint64_t read_u64_le(const uint8_t *ptr, size_t offset) { + return (uint64_t) ptr[offset + 0] << 0 | // + (uint64_t) ptr[offset + 1] << 8 | // + (uint64_t) ptr[offset + 2] << 16 | // + (uint64_t) ptr[offset + 3] << 24 | // + (uint64_t) ptr[offset + 4] << 32 | // + (uint64_t) ptr[offset + 5] << 40 | // + (uint64_t) ptr[offset + 6] << 48 | // + (uint64_t) ptr[offset + 7] << 56; +} diff --git a/src/common/read.h b/src/common/read.h new file mode 100644 index 0000000..61cfa8c --- /dev/null +++ b/src/common/read.h @@ -0,0 +1,82 @@ +#pragma once + +#include // uint*_t +#include // size_t + +/** + * Read 2 bytes as Big Endian from byte buffer. + * + * @param[in] ptr + * Pointer to byte buffer. + * @param[in] offset + * Offset in the byte buffer. + * + * @return 2 bytes value read from buffer. + * + */ +uint16_t read_u16_be(const uint8_t *ptr, size_t offset); + +/** + * Read 4 bytes as Big Endian from byte buffer. + * + * @param[in] ptr + * Pointer to byte buffer. + * @param[in] offset + * Offset in the byte buffer. + * + * @return 4 bytes value read from buffer. + * + */ +uint32_t read_u32_be(const uint8_t *ptr, size_t offset); + +/** + * Read 8 bytes as Big Endian from byte buffer. + * + * @param[in] ptr + * Pointer to byte buffer. + * @param[in] offset + * Offset in the byte buffer. + * + * @return 8 bytes value read from buffer. + * + */ +uint64_t read_u64_be(const uint8_t *ptr, size_t offset); + +/** + * Read 2 bytes as Little Endian from byte buffer. + * + * @param[in] ptr + * Pointer to byte buffer. + * @param[in] offset + * Offset in the byte buffer. + * + * @return 2 bytes value read from buffer. + * + */ +uint16_t read_u16_le(const uint8_t *ptr, size_t offset); + +/** + * Read 4 bytes as Little Endian from byte buffer. + * + * @param[in] ptr + * Pointer to byte buffer. + * @param[in] offset + * Offset in the byte buffer. + * + * @return 4 bytes value read from buffer. + * + */ +uint32_t read_u32_le(const uint8_t *ptr, size_t offset); + +/** + * Read 8 bytes as Little Endian from byte buffer. + * + * @param[in] ptr + * Pointer to byte buffer. + * @param[in] offset + * Offset in the byte buffer. + * + * @return 8 bytes value read from buffer. + * + */ +uint64_t read_u64_le(const uint8_t *ptr, size_t offset); diff --git a/src/common/varint.c b/src/common/varint.c new file mode 100644 index 0000000..d04fbb2 --- /dev/null +++ b/src/common/varint.c @@ -0,0 +1,101 @@ +/***************************************************************************** + * (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 // uint*_t +#include // size_t +#include // bool + +#include "varint.h" +#include "write.h" +#include "read.h" + +uint8_t varint_size(uint64_t value) { + if (value <= 0xFC) { + return 1; + } + + if (value <= UINT16_MAX) { + return 3; + } + + if (value <= UINT32_MAX) { + return 5; + } + + return 9; // <= UINT64_MAX +} + +int varint_read(const uint8_t *in, size_t in_len, uint64_t *value) { + if (in_len < 1) { + return -1; + } + + uint8_t prefix = in[0]; + + if (prefix == 0xFD) { + if (in_len < 3) { + return -1; + } + *value = (uint64_t) read_u16_le(in, 1); + return 3; + } + + if (prefix == 0xFE) { + if (in_len < 5) { + return -1; + } + *value = (uint64_t) read_u32_le(in, 1); + return 5; + } + + if (prefix == 0xFF) { + if (in_len < 9) { + return -1; + } + *value = (uint64_t) read_u64_le(in, 1); + return 9; + } + + *value = (uint64_t) prefix; // prefix <= 0xFC + + return 1; +} + +int varint_write(uint8_t *out, size_t offset, uint64_t value) { + uint8_t varint_len = varint_size(value); + + switch (varint_len) { + case 1: + out[offset] = (uint8_t) value; + break; + case 3: + out[offset++] = 0xFD; + write_u16_le(out, offset, (uint16_t) value); + break; + case 5: + out[offset++] = 0xFE; + write_u32_le(out, offset, (uint32_t) value); + break; + case 9: + out[offset++] = 0xFF; + write_u64_le(out, offset, (uint64_t) value); + break; + default: + return -1; + } + + return varint_len; +} diff --git a/src/common/varint.h b/src/common/varint.h new file mode 100644 index 0000000..80aeb43 --- /dev/null +++ b/src/common/varint.h @@ -0,0 +1,52 @@ +#pragma once + +#include // uint*_t +#include // size_t +#include // bool + +/** + * Size of value represented as Bitcoin-like varint. + * + * @see https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer + * + * @param[in] value + * 64-bit unsigned integer to compute varint size. + * + * @return number of bytes to write value as varint (1, 3, 5 or 9 bytes). + * + */ +uint8_t varint_size(uint64_t value); + +/** + * Read Bitcoin-like varint from byte buffer. + * + * @see https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer + * + * @param[in] in + * Pointer to input byte buffer. + * @param[in] in_len + * Length of the input byte buffer. + * @param[out] value + * Pointer to 64-bit unsigned integer to output varint. + * + * @return number of bytes read (1, 3, 5 or 9 bytes), -1 otherwise. + * + */ +int varint_read(const uint8_t *in, size_t in_len, uint64_t *value); + +/** + * Write Bitcoin-like varint to byte buffer. + * + * @see https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer + * + * @param[out] out + * Pointer to output byte buffer. + * @param[in] offset + * Offset in the output byte buffer. + * @param[in] value + * 64-bit unsigned integer to write as varint. + * + * @return number of bytes written (1, 3, 5 or 9 bytes), -1 otherwise. + * + */ +int varint_write(uint8_t *out, size_t offset, uint64_t value); diff --git a/src/common/write.c b/src/common/write.c new file mode 100644 index 0000000..90ec141 --- /dev/null +++ b/src/common/write.c @@ -0,0 +1,64 @@ +/***************************************************************************** + * (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 // uint*_t +#include // size_t + +void write_u16_be(uint8_t *ptr, size_t offset, uint16_t value) { + ptr[offset + 0] = (uint8_t)(value >> 8); + ptr[offset + 1] = (uint8_t)(value >> 0); +} + +void write_u32_be(uint8_t *ptr, size_t offset, uint32_t value) { + ptr[offset + 0] = (uint8_t)(value >> 24); + ptr[offset + 1] = (uint8_t)(value >> 16); + ptr[offset + 2] = (uint8_t)(value >> 8); + ptr[offset + 3] = (uint8_t)(value >> 0); +} + +void write_u64_be(uint8_t *ptr, size_t offset, uint64_t value) { + ptr[offset + 0] = (uint8_t)(value >> 56); + ptr[offset + 1] = (uint8_t)(value >> 48); + ptr[offset + 2] = (uint8_t)(value >> 40); + ptr[offset + 3] = (uint8_t)(value >> 32); + ptr[offset + 4] = (uint8_t)(value >> 24); + ptr[offset + 5] = (uint8_t)(value >> 16); + ptr[offset + 6] = (uint8_t)(value >> 8); + ptr[offset + 7] = (uint8_t)(value >> 0); +} + +void write_u16_le(uint8_t *ptr, size_t offset, uint16_t value) { + ptr[offset + 0] = (uint8_t)(value >> 0); + ptr[offset + 1] = (uint8_t)(value >> 8); +} + +void write_u32_le(uint8_t *ptr, size_t offset, uint32_t value) { + ptr[offset + 0] = (uint8_t)(value >> 0); + ptr[offset + 1] = (uint8_t)(value >> 8); + ptr[offset + 2] = (uint8_t)(value >> 16); + ptr[offset + 3] = (uint8_t)(value >> 24); +} + +void write_u64_le(uint8_t *ptr, size_t offset, uint64_t value) { + ptr[offset + 0] = (uint8_t)(value >> 0); + ptr[offset + 1] = (uint8_t)(value >> 8); + ptr[offset + 2] = (uint8_t)(value >> 16); + ptr[offset + 3] = (uint8_t)(value >> 24); + ptr[offset + 4] = (uint8_t)(value >> 32); + ptr[offset + 5] = (uint8_t)(value >> 40); + ptr[offset + 6] = (uint8_t)(value >> 48); + ptr[offset + 7] = (uint8_t)(value >> 56); +} diff --git a/src/common/write.h b/src/common/write.h new file mode 100644 index 0000000..0418f5c --- /dev/null +++ b/src/common/write.h @@ -0,0 +1,82 @@ +#pragma once + +#include // uint*_t +#include // size_t + +/** + * Write 16-bit unsigned integer value as Big Endian. + * + * @param[out] ptr + * Pointer to output byte buffer. + * @param[in] offset + * Offset in the output byte buffer. + * @param[in] value + * 16-bit unsigned integer to write in output byte buffer as Big Endian. + * + */ +void write_u16_be(const uint8_t *ptr, size_t offset, uint16_t value); + +/** + * Write 32-bit unsigned integer value as Big Endian. + * + * @param[out] ptr + * Pointer to output byte buffer. + * @param[in] offset + * Offset in the output byte buffer. + * @param[in] value + * 32-bit unsigned integer to write in output byte buffer as Big Endian. + * + */ +void write_u32_be(uint8_t *ptr, size_t offset, uint32_t value); + +/** + * Write 64-bit unsigned integer value as Big Endian. + * + * @param[out] ptr + * Pointer to output byte buffer. + * @param[in] offset + * Offset in the output byte buffer. + * @param[in] value + * 64-bit unsigned integer to write in output byte buffer as Big Endian. + * + */ +void write_u64_be(uint8_t *ptr, size_t offset, uint64_t value); + +/** + * Write 16-bit unsigned integer value as Little Endian. + * + * @param[out] ptr + * Pointer to output byte buffer. + * @param[in] offset + * Offset in the output byte buffer. + * @param[in] value + * 16-bit unsigned integer to write in output byte buffer as Little Endian. + * + */ +void write_u16_le(uint8_t *ptr, size_t offset, uint16_t value); + +/** + * Write 32-bit unsigned integer value as Little Endian. + * + * @param[out] ptr + * Pointer to output byte buffer. + * @param[in] offset + * Offset in the output byte buffer. + * @param[in] value + * 32-bit unsigned integer to write in output byte buffer as Little Endian. + * + */ +void write_u32_le(uint8_t *ptr, size_t offset, uint32_t value); + +/** + * Write 64-bit unsigned integer value as Little Endian. + * + * @param[out] ptr + * Pointer to output byte buffer. + * @param[in] offset + * Offset in the output byte buffer. + * @param[in] value + * 64-bit unsigned integer to write in output byte buffer as Little Endian. + * + */ +void write_u64_le(uint8_t *ptr, size_t offset, uint64_t value); diff --git a/src/helper/send_reponse.c b/src/helper/send_reponse.c index 19f9c39..79656e6 100644 --- a/src/helper/send_reponse.c +++ b/src/helper/send_reponse.c @@ -27,27 +27,35 @@ #include "../sw.h" int helper_send_response_pubkey() { - uint8_t resp[1 + PUBKEY_LEN + 1 + CHAINCODE_LEN] = {0}; + //uint8_t resp[1 + PUBKEY_LEN + 1 + CHAINCODE_LEN] = {0}; + uint8_t resp[PUBKEY_LEN] = {0}; size_t offset = 0; - resp[offset++] = PUBKEY_LEN; + /*resp[offset++] = PUBKEY_LEN; memmove(resp + offset, G_context.pk_info.raw_public_key, PUBKEY_LEN); offset += PUBKEY_LEN; resp[offset++] = CHAINCODE_LEN; memmove(resp + offset, G_context.pk_info.chain_code, CHAINCODE_LEN); - offset += CHAINCODE_LEN; + offset += CHAINCODE_LEN;*/ + + memmove(resp + offset, G_context.pk_info.raw_public_key, PUBKEY_LEN); + offset += PUBKEY_LEN; return io_send_response_pointer(resp, offset, SW_OK); } int helper_send_response_sig() { - uint8_t resp[1 + MAX_DER_SIG_LEN + 1] = {0}; + /*uint8_t resp[1 + MAX_DER_SIG_LEN + 1] = {0}; size_t offset = 0; resp[offset++] = G_context.tx_info.signature_len; memmove(resp + offset, G_context.tx_info.signature, G_context.tx_info.signature_len); offset += G_context.tx_info.signature_len; - resp[offset++] = (uint8_t) G_context.tx_info.v; + resp[offset++] = (uint8_t) G_context.tx_info.v;*/ + + uint8_t resp[PUBKEY_LEN] = G_context.tx_info.signature; + size_t offset = G_context.tx_info.signature_len; + return io_send_response_pointer(resp, offset, SW_OK); }