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 EIP 1024 APDUs #240

Merged
merged 4 commits into from
Apr 7, 2022
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ APP_LOAD_PARAMS += --path "1517992542'/1101353413'"

APPVERSION_M=1
APPVERSION_N=9
APPVERSION_P=17
APPVERSION_P=18
APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)
APP_LOAD_FLAGS= --appFlags 0x240 --dep Ethereum:$(APPVERSION)

Expand Down
51 changes: 50 additions & 1 deletion doc/ethapp.asc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Ethereum application : Common Technical Specifications
=======================================================
Ledger Firmware Team <hello@ledger.fr>
Application version 1.5.0 - 25th of September 2020
Application version 1.9.18 - 29th of January 2022

## 1.0
- Initial release
Expand All @@ -26,6 +26,9 @@ Application version 1.5.0 - 25th of September 2020
## 1.9.13
- Add SET PLUGIN

## 1.9.17
- Add PERFORM PRIVACY OPERATION

## About

This application describes the APDU messages interface to communicate with the Ethereum application.
Expand Down Expand Up @@ -379,6 +382,52 @@ type || version || len(pluginName) || pluginName || address || selector || chain

None

### PERFORM PRIVACY OPERATION

#### Description

This command performs privacy operations as defined in EIP 1024 (https://ethereum-magicians.org/t/eip-1024-cross-client-encrypt-decrypt/505)

It can return the public encryption key on Curve25519 for a given Ethereum account or the shared secret (generated by the scalar multiplication of the remote public key by the account private key on Curve25519) used to decrypt private data encrypted for a given Ethereum account

All data can be optionally checked on the device before being returned.

#### Coding

'Command'

[width="80%"]
|==============================================================================================================================
| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le*
| E0 | 18 | 00 : return data

01 : display data and confirm before returning
| 00 : return the public encryption key

01 : return the shared secret | variable | variable
|==============================================================================================================================

'Input data'

[width="80%"]
|==============================================================================================================================
| *Description* | *Length*
| Number of BIP 32 derivations to perform (max 10) | 1
| First derivation index (big endian) | 4
| ... | 4
| Last derivation index (big endian) | 4
| Third party public key on Curve25519, if returning the shared secret | 32
|==============================================================================================================================

'Output data'

[width="80%"]
|==============================================================================================================================
| *Description* | *Length*
| Public encryption key or shared secret | 32
|==============================================================================================================================


### GET APP CONFIGURATION

#### Description
Expand Down
8 changes: 8 additions & 0 deletions src/apdu_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#define INS_SET_EXTERNAL_PLUGIN 0x12
#define INS_PROVIDE_NFT_INFORMATION 0x14
#define INS_SET_PLUGIN 0x16
#define INS_PERFORM_PRIVACY_OPERATION 0x18
#define P1_CONFIRM 0x01
#define P1_NON_CONFIRM 0x00
#define P2_NO_CHAINCODE 0x00
Expand Down Expand Up @@ -114,6 +115,13 @@ void handleSetPlugin(uint8_t p1,
unsigned int *flags,
unsigned int *tx);

void handlePerformPrivacyOperation(uint8_t p1,
uint8_t p2,
uint8_t *workBuffer,
uint16_t dataLength,
unsigned int *flags,
unsigned int *tx);

#ifdef HAVE_ETH2

void handleGetEth2PublicKey(uint8_t p1,
Expand Down
9 changes: 9 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,15 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
tx);
break;

case INS_PERFORM_PRIVACY_OPERATION:
handlePerformPrivacyOperation(G_io_apdu_buffer[OFFSET_P1],
G_io_apdu_buffer[OFFSET_P2],
G_io_apdu_buffer + OFFSET_CDATA,
G_io_apdu_buffer[OFFSET_LC],
flags,
tx);
break;

case INS_SIGN:
handleSign(G_io_apdu_buffer[OFFSET_P1],
G_io_apdu_buffer[OFFSET_P2],
Expand Down
2 changes: 2 additions & 0 deletions src/ui_callbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ unsigned int io_seproxyhal_touch_data_cancel(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_signMessage712_v0_ok(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_signMessage712_v0_cancel(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_eth2_address_ok(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_privacy_ok(const bagl_element_t *e);
unsigned int io_seproxyhal_touch_privacy_cancel(const bagl_element_t *e);

void ui_idle(void);
void ui_warning_contract_data(void);
Expand Down
4 changes: 4 additions & 0 deletions src/ui_flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ extern const ux_flow_step_t* const ux_sign_712_v0_flow[];

extern const ux_flow_step_t* const ux_display_public_eth2_flow[];

extern const ux_flow_step_t* const ux_display_privacy_public_key_flow[];

extern const ux_flow_step_t* const ux_display_privacy_shared_secret_flow[];

#ifdef HAVE_STARKWARE

extern const ux_flow_step_t* const ux_display_stark_public_flow[];
Expand Down
122 changes: 122 additions & 0 deletions src_features/performPrivacyOperation/cmd_performPrivacyOperation.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include "shared_context.h"
#include "apdu_constants.h"

#include "ui_flow.h"
#include "feature_performPrivacyOperation.h"

#define P2_PUBLIC_ENCRYPTION_KEY 0x00
#define P2_SHARED_SECRET 0x01

void decodeScalar(const uint8_t *scalarIn, uint8_t *scalarOut) {
for (uint8_t i = 0; i < 32; i++) {
switch (i) {
case 0:
scalarOut[0] = (scalarIn[31] & 0x7f) | 0x40;
break;
case 31:
scalarOut[31] = scalarIn[0] & 0xf8;
break;
cseguret-ledger marked this conversation as resolved.
Show resolved Hide resolved
default:
scalarOut[i] = scalarIn[31 - i];
}
}
cseguret-ledger marked this conversation as resolved.
Show resolved Hide resolved
}

void handlePerformPrivacyOperation(uint8_t p1,
uint8_t p2,
uint8_t *dataBuffer,
uint16_t dataLength,
unsigned int *flags,
unsigned int *tx) {
UNUSED(dataLength);
uint8_t privateKeyData[INT256_LENGTH];
uint8_t privateKeyDataSwapped[INT256_LENGTH];
uint32_t bip32Path[MAX_BIP32_PATH];
uint8_t bip32PathLength = *(dataBuffer++);
cx_err_t status = CX_OK;
if (p2 == P2_PUBLIC_ENCRYPTION_KEY) {
if (dataLength < 1 + 4 * bip32PathLength) {
THROW(0x6700);
}
} else if (p2 == P2_SHARED_SECRET) {
if (dataLength < 1 + 4 * bip32PathLength + 32) {
THROW(0x6700);
}
} else {
THROW(0x6B00);
}
cx_ecfp_private_key_t privateKey;
if ((bip32PathLength < 0x01) || (bip32PathLength > MAX_BIP32_PATH)) {
PRINTF("Invalid path\n");
THROW(0x6a80);
cseguret-ledger marked this conversation as resolved.
Show resolved Hide resolved
}
if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) {
THROW(0x6B00);
cseguret-ledger marked this conversation as resolved.
Show resolved Hide resolved
}
for (uint8_t i = 0; i < bip32PathLength; i++) {
bip32Path[i] = U4BE(dataBuffer, 0);
dataBuffer += 4;
}
os_perso_derive_node_bip32(
CX_CURVE_256K1,
bip32Path,
bip32PathLength,
privateKeyData,
(tmpCtx.publicKeyContext.getChaincode ? tmpCtx.publicKeyContext.chainCode : NULL));
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);
cx_ecfp_generate_pair(CX_CURVE_256K1, &tmpCtx.publicKeyContext.publicKey, &privateKey, 1);
getEthAddressStringFromKey(&tmpCtx.publicKeyContext.publicKey,
tmpCtx.publicKeyContext.address,
&global_sha3,
chainConfig->chainId);
if (p2 == P2_PUBLIC_ENCRYPTION_KEY) {
decodeScalar(privateKeyData, privateKeyDataSwapped);
cx_ecfp_init_private_key(CX_CURVE_Curve25519, privateKeyDataSwapped, 32, &privateKey);
cx_ecfp_generate_pair(CX_CURVE_Curve25519,
&tmpCtx.publicKeyContext.publicKey,
&privateKey,
1);
explicit_bzero(privateKeyDataSwapped, sizeof(privateKeyDataSwapped));
} else {
memmove(tmpCtx.publicKeyContext.publicKey.W + 1, dataBuffer, 32);
status = cx_x25519(tmpCtx.publicKeyContext.publicKey.W + 1, privateKeyData, 32);
}
explicit_bzero(&privateKey, sizeof(privateKey));
explicit_bzero(privateKeyData, sizeof(privateKeyData));

if (status != CX_OK) {
THROW(0x6A80);
cseguret-ledger marked this conversation as resolved.
Show resolved Hide resolved
}

#ifndef NO_CONSENT
if (p1 == P1_NON_CONFIRM)
#endif // NO_CONSENT
{
*tx = set_result_perform_privacy_operation();
THROW(0x9000);
cseguret-ledger marked this conversation as resolved.
Show resolved Hide resolved
}
#ifndef NO_CONSENT
else {
snprintf(strings.common.fullAddress,
sizeof(strings.common.fullAddress),
"0x%.*s",
40,
tmpCtx.publicKeyContext.address);
for (uint8_t i = 0; i < 32; i++) {
privateKeyData[i] = tmpCtx.publicKeyContext.publicKey.W[32 - i];
}
snprintf(strings.common.fullAmount,
sizeof(strings.common.fullAmount) - 1,
"%.*H",
32,
privateKeyData);
if (p2 == P2_PUBLIC_ENCRYPTION_KEY) {
ux_flow_init(0, ux_display_privacy_public_key_flow, NULL);
} else {
ux_flow_init(0, ux_display_privacy_shared_secret_flow, NULL);
}

*flags |= IO_ASYNCH_REPLY;
}
#endif // NO_CONSENT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "shared_context.h"

uint32_t set_result_perform_privacy_operation(void);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "shared_context.h"

uint32_t set_result_perform_privacy_operation() {
for (uint8_t i = 0; i < 32; i++) {
G_io_apdu_buffer[i] = tmpCtx.publicKeyContext.publicKey.W[32 - i];
}
return 32;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "shared_context.h"
#include "feature_getPublicKey.h"
#include "ui_callbacks.h"

unsigned int io_seproxyhal_touch_privacy_ok(__attribute__((unused)) const bagl_element_t *e) {
cseguret-ledger marked this conversation as resolved.
Show resolved Hide resolved
uint32_t tx = set_result_perform_privacy_operation();
G_io_apdu_buffer[tx++] = 0x90;
G_io_apdu_buffer[tx++] = 0x00;
cseguret-ledger marked this conversation as resolved.
Show resolved Hide resolved
reset_app_context();
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx);
// Display back the original UX
ui_idle();
return 0; // do not redraw the widget
}

unsigned int io_seproxyhal_touch_privacy_cancel(__attribute__((unused)) const bagl_element_t *e) {
G_io_apdu_buffer[0] = 0x69;
G_io_apdu_buffer[1] = 0x85;
cseguret-ledger marked this conversation as resolved.
Show resolved Hide resolved
reset_app_context();
// Send back the response, do not restart the event loop
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
// Display back the original UX
ui_idle();
return 0; // do not redraw the widget
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include "shared_context.h"
#include "ui_callbacks.h"

// clang-format off
UX_STEP_NOCB(
ux_display_privacy_public_key_flow_1_step,
pnn,
{
&C_icon_eye,
"Provide public",
"privacy key",
});
UX_STEP_NOCB(
ux_display_privacy_public_key_flow_2_step,
bnnn_paging,
{
.title = "Address",
.text = strings.common.fullAddress,
});
UX_STEP_NOCB(
ux_display_privacy_public_key_flow_3_step,
bnnn_paging,
{
.title = "Key",
.text = strings.common.fullAmount,
});
UX_STEP_CB(
ux_display_privacy_public_key_flow_4_step,
pb,
io_seproxyhal_touch_privacy_ok(NULL),
{
&C_icon_validate_14,
"Approve",
});
UX_STEP_CB(
ux_display_privacy_public_key_flow_5_step,
pb,
io_seproxyhal_touch_privacy_cancel(NULL),
{
&C_icon_crossmark,
"Reject",
});

UX_STEP_NOCB(
ux_display_privacy_shared_secret_flow_1_step,
pnn,
{
&C_icon_eye,
"Provide privacy",
"secret key",
});

// clang-format on

UX_FLOW(ux_display_privacy_public_key_flow,
&ux_display_privacy_public_key_flow_1_step,
&ux_display_privacy_public_key_flow_2_step,
&ux_display_privacy_public_key_flow_3_step,
&ux_display_privacy_public_key_flow_4_step,
&ux_display_privacy_public_key_flow_5_step);

UX_FLOW(ux_display_privacy_shared_secret_flow,
&ux_display_privacy_shared_secret_flow_1_step,
&ux_display_privacy_public_key_flow_2_step,
&ux_display_privacy_public_key_flow_3_step,
&ux_display_privacy_public_key_flow_4_step,
&ux_display_privacy_public_key_flow_5_step);