Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Soroban support. #81

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 8 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
{
"files.associations": {
"*.h": "c",
"random": "c"
"random": "c",
"ratio": "c",
"system_error": "c",
"array": "c",
"functional": "c",
"tuple": "c",
"type_traits": "c",
"utility": "c"
},
"C_Cpp.clang_format_path": "/usr/bin/clang-format",
"editor.formatOnSave": true
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ include $(BOLOS_SDK)/Makefile.defines

APPNAME = Stellar
APPVERSION_M=5
APPVERSION_N=0
APPVERSION_P=3
APPVERSION_N=1
APPVERSION_P=0
APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)

ifeq ($(TARGET_NAME), TARGET_NANOS)
Expand Down
15 changes: 15 additions & 0 deletions docs/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,20 @@
| ----------------------- | ------ | ---------------- |
| 64 | 0x9000 | `signature (64)` |

## INS_SIGN_SOROBAN_AUTHORATION

### Command

| CLA | INS | P1 | P2 | Lc | CData |
| ---- | ---- | ---------------------------------- | ---------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| 0xE0 | 0x0A | 0x00 (first) <br> 0x80 (not_first) | 0x00 (last) <br> 0x80 (more) | 1 + 4n + k<br/>Only the first data chunk contains bip32 path data | `len(bip32_path) (1)` \|\|<br> `bip32_path{1} (4)` \|\|<br>`...` \|\|<br>`bip32_path{n} (4)` \|\|<br> `hash_id_preimage_chunk(k)` |

### Response

| Response length (bytes) | SW | RData |
| ----------------------- | ------ | ---------------- |
| 64 | 0x9000 | `signature (64)` |

## Status Words

| SW | SW name | Description |
Expand All @@ -76,6 +90,7 @@
| 0x6C24 | `SW_UNKNOWN_OP` | Unknown Stellar operation |
| 0x6C25 | `SW_UNKNOWN_ENVELOPE_TYPE` | Unknown Stellar envelope type |
| 0x6C66 | `SW_TX_HASH_SIGNING_MODE_NOT_ENABLED` | Hash signing model not enabled |
| 0x6C66 | `SW_CUSTOM_CONTRACT_MODE_NOT_ENABLED` | Custom contract model not enabled |
| 0x6D00 | `SW_INS_NOT_SUPPORTED` | No command exists with `INS` |
| 0x6E00 | `SW_CLA_NOT_SUPPORTED` | Bad `CLA` used for this application |
| 0xB000 | `SW_WRONG_RESPONSE_LENGTH` | Wrong response length (buffer too small or too big) |
Expand Down
7 changes: 7 additions & 0 deletions release-notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Release Notes

## v5.1.0

### Updated
- Add support for Stellar Soroban.
- Fix some bugs.
- Remove the soon-to-be-deprecated SDK calls.

## v4.0.0

### Updated
Expand Down
15 changes: 14 additions & 1 deletion src/apdu/dispatcher.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,21 @@ int apdu_dispatcher(const command_t *cmd) {
buf.ptr = cmd->data;
buf.size = cmd->lc;
buf.offset = 0;

return handler_sign_tx(&buf, !cmd->p1, (bool) (cmd->p2 & P2_MORE));
case INS_SIGN_SOROBAN_AUTHORATION:
if ((cmd->p1 != P1_FIRST && cmd->p1 != P1_MORE) ||
(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 handle_sign_soroban_authorization(&buf, !cmd->p1, (bool) (cmd->p2 & P2_MORE));
default:
return io_send_sw(SW_INS_NOT_SUPPORTED);
}
Expand Down
109 changes: 51 additions & 58 deletions src/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,36 +26,28 @@
int crypto_derive_private_key(cx_ecfp_private_key_t *private_key,
const uint32_t *bip32_path,
uint8_t bip32_path_len) {
uint8_t raw_private_key[RAW_ED25519_PRIVATE_KEY_SIZE] = {0};
int error = 0;
uint8_t raw_private_key[64] = {0};
cx_err_t error = CX_OK;

BEGIN_TRY {
TRY {
// derive the seed with bip32_path
os_perso_derive_node_with_seed_key(HDW_ED25519_SLIP10,
CX_CURVE_Ed25519,
bip32_path,
bip32_path_len,
raw_private_key,
NULL,
(unsigned char *) STELLAR_SEED_KEY,
sizeof(STELLAR_SEED_KEY));
// new private_key from raw
cx_ecfp_init_private_key(CX_CURVE_Ed25519,
raw_private_key,
sizeof(raw_private_key),
private_key);
}
CATCH_OTHER(e) {
error = e;
}
FINALLY {
explicit_bzero(&raw_private_key, sizeof(raw_private_key));
}
}
END_TRY;
// derive the seed with bip32_path
CX_CHECK(os_derive_bip32_with_seed_no_throw(HDW_ED25519_SLIP10,
CX_CURVE_Ed25519,
bip32_path,
bip32_path_len,
raw_private_key,
NULL,
(unsigned char *) STELLAR_SEED_KEY,
sizeof(STELLAR_SEED_KEY)));
// new private_key from raw
CX_CHECK(cx_ecfp_init_private_key_no_throw(CX_CURVE_Ed25519, raw_private_key, 32, private_key));

return error;
end:
explicit_bzero(&raw_private_key, sizeof(raw_private_key));
if (error != CX_OK) {
explicit_bzero(private_key, sizeof(*private_key));
return -1;
}
return 0;
}

// converts little endian 32 byte public key to big endian 32 byte public key
Expand All @@ -71,48 +63,49 @@ void raw_public_key_le_to_be(cx_ecfp_public_key_t *public_key,
}
}

void crypto_init_public_key(cx_ecfp_private_key_t *private_key,
cx_ecfp_public_key_t *public_key,
uint8_t raw_public_key[static RAW_ED25519_PUBLIC_KEY_SIZE]) {
int crypto_init_public_key(cx_ecfp_private_key_t *private_key,
cx_ecfp_public_key_t *public_key,
uint8_t raw_public_key[static RAW_ED25519_PUBLIC_KEY_SIZE]) {
cx_err_t error = CX_OK;

// generate corresponding public key
cx_ecfp_generate_pair(CX_CURVE_Ed25519, public_key, private_key, 1);
CX_CHECK(cx_ecfp_generate_pair_no_throw(CX_CURVE_Ed25519, public_key, private_key, 1));

end:
if (error != CX_OK) {
return -1;
}
raw_public_key_le_to_be(public_key, raw_public_key);
return 0;
}

int crypto_sign_message(const uint8_t *message,
uint8_t message_len,
const uint8_t *signature,
uint8_t signature_len) {
cx_ecfp_private_key_t private_key = {0};
cx_err_t error = CX_OK;

// derive private key according to BIP32 path
int error =
int ret =
crypto_derive_private_key(&private_key, G_context.bip32_path, G_context.bip32_path_len);
if (error != 0) {
return error;
if (ret != 0) {
return -1;
}

BEGIN_TRY {
TRY {
cx_eddsa_sign(&private_key,
CX_LAST,
CX_SHA512,
message,
message_len,
NULL,
0,
(unsigned char *) signature,
signature_len,
NULL);
PRINTF("Signature: %.*H\n", signature_len, signature);
}
CATCH_OTHER(e) {
error = e;
}
FINALLY {
explicit_bzero(&private_key, sizeof(private_key));
}
CX_CHECK(cx_eddsa_sign_no_throw(&private_key,
CX_SHA512,
message,
message_len,
(unsigned char *) signature,
signature_len));
PRINTF("Signature: %.*H\n", signature_len, signature);

end:
explicit_bzero(&private_key, sizeof(private_key));
if (error != CX_OK) {
PRINTF("In crypto_sign_message: ERROR %x \n", error);
return -1;
}
END_TRY;
return error;
}
return 0;
}
10 changes: 5 additions & 5 deletions src/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ int crypto_derive_private_key(cx_ecfp_private_key_t *private_key,
* @param[out] raw_public_key
* Pointer to raw public key.
*
* @throw INVALID_PARAMETER
* @return 0 on success, error number otherwise.
*
*/
void crypto_init_public_key(cx_ecfp_private_key_t *private_key,
cx_ecfp_public_key_t *public_key,
uint8_t raw_public_key[static RAW_ED25519_PUBLIC_KEY_SIZE]);
int crypto_init_public_key(cx_ecfp_private_key_t *private_key,
cx_ecfp_public_key_t *public_key,
uint8_t raw_public_key[static RAW_ED25519_PUBLIC_KEY_SIZE]);

/**
* Sign message.
Expand All @@ -49,4 +49,4 @@ void crypto_init_public_key(cx_ecfp_private_key_t *private_key,
int crypto_sign_message(const uint8_t *message,
uint8_t message_len,
const uint8_t *signature,
uint8_t signature_len);
uint8_t signature_len);
11 changes: 6 additions & 5 deletions src/handler/get_public_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ int handler_get_public_key(buffer_t *cdata, bool display) {
}

// derive private key according to BIP32 path
int error =
crypto_derive_private_key(&private_key, G_context.bip32_path, G_context.bip32_path_len);
if (error != 0) {
return io_send_sw(error);

if (crypto_derive_private_key(&private_key, G_context.bip32_path, G_context.bip32_path_len)) {
return io_send_sw(SW_INTERNAL_ERROR);
}
// generate corresponding public key
crypto_init_public_key(&private_key, &public_key, G_context.raw_public_key);
if (crypto_init_public_key(&private_key, &public_key, G_context.raw_public_key)) {
return io_send_sw(SW_INTERNAL_ERROR);
};
// reset private key
explicit_bzero(&private_key, sizeof(private_key));

Expand Down
16 changes: 16 additions & 0 deletions src/handler/handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,19 @@ int handler_sign_tx(buffer_t *cdata, bool is_first_chunk, bool more);
*
*/
int handler_sign_tx_hash(buffer_t *cdata);

/**
* Handler for INS_SIGN_SOROBAN_AUTHORATION command. If successfully parse BIP32 path
* and soroban authorization, sign soroban authorization and send APDU response.
*
* @param[in,out] cdata
* Command data with BIP32 path and soroban authorization serialized.
* @param[in] is_first_chunk
* Is the first data chunk
* @param[in] more
* Whether more APDU chunk to be received or not.
*
* @return zero or positive integer if success, negative integer otherwise.
*
*/
int handle_sign_soroban_authorization(buffer_t *cdata, bool is_first_chunk, bool more);
93 changes: 93 additions & 0 deletions src/handler/sign_soroban_authorization.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*****************************************************************************
* Ledger Stellar App.
* (c) 2022 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 "./handler.h"
#include "../globals.h"
#include "../types.h"
#include "../sw.h"
#include "../send_response.h"
#include "../crypto.h"
#include "../ui/ui.h"
#include "../swap/swap_lib_calls.h"
#include "../swap/handle_swap_commands.h"
#include "../transaction/transaction_parser.h"

int handle_sign_soroban_authorization(buffer_t *cdata, bool is_first_chunk, bool more) {
if (is_first_chunk) {
explicit_bzero(&G_context, sizeof(G_context));
}

if (G_context.auth.raw_size + cdata->size > RAW_TX_MAX_SIZE) {
return io_send_sw(SW_WRONG_TX_LENGTH);
}

if (is_first_chunk) {
G_context.req_type = CONFIRM_SOROBAN_AUTHORATION;
G_context.state = STATE_NONE;

if (!buffer_read_u8(cdata, &G_context.bip32_path_len) ||
!buffer_read_bip32_path(cdata,
G_context.bip32_path,
(size_t) G_context.bip32_path_len)) {
return io_send_sw(SW_WRONG_DATA_LENGTH);
}
size_t data_length = cdata->size - cdata->offset;
memcpy(G_context.auth.raw, cdata->ptr + cdata->offset, data_length);
G_context.auth.raw_size += data_length;
} else {
if (G_context.req_type != CONFIRM_SOROBAN_AUTHORATION) {
return io_send_sw(SW_BAD_STATE);
}
memcpy(G_context.auth.raw + G_context.auth.raw_size, cdata->ptr, cdata->size);
G_context.auth.raw_size += cdata->size;
}

PRINTF("data size: %d\n", G_context.auth.raw_size);

if (more) {
return io_send_sw(SW_OK);
}

if (!parse_auth(G_context.auth.raw, G_context.auth.raw_size, &G_context.auth)) {
THROW(SW_TX_PARSING_FAIL);
}

G_context.state = STATE_PARSED;
PRINTF("auth parsed.\n");

cx_ecfp_private_key_t private_key = {0};
cx_ecfp_public_key_t public_key = {0};

// derive private key according to BIP32 path
if (crypto_derive_private_key(&private_key, G_context.bip32_path, G_context.bip32_path_len)) {
explicit_bzero(&private_key, sizeof(private_key));
return io_send_sw(SW_INTERNAL_ERROR);
}
// generate corresponding public key
if (crypto_init_public_key(&private_key, &public_key, G_context.raw_public_key)) {
return io_send_sw(SW_INTERNAL_ERROR);
}
// reset private key
explicit_bzero(&private_key, sizeof(private_key));

if (cx_hash_sha256(G_context.auth.raw, G_context.auth.raw_size, G_context.hash, HASH_SIZE) !=
HASH_SIZE) {
THROW(SW_TX_HASH_FAIL);
}

return ui_approve_soroban_auth_init();
};
2 changes: 1 addition & 1 deletion src/handler/sign_transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ int handler_sign_tx(buffer_t *cdata, bool is_first_chunk, bool more) {
crypto_derive_private_key(&private_key, G_context.bip32_path, G_context.bip32_path_len);
if (error != 0) {
explicit_bzero(&private_key, sizeof(private_key));
return io_send_sw(error);
return io_send_sw(SW_INTERNAL_ERROR);
}
// generate corresponding public key
crypto_init_public_key(&private_key, &public_key, G_context.raw_public_key);
Expand Down
Loading
Loading