Merge pull request #240 from btchip/eip1024

Add EIP 1024 APDUs
This commit is contained in:
Coline Seguret
2022-04-07 10:26:58 +02:00
committed by GitHub
11 changed files with 300 additions and 2 deletions

View File

@@ -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)

View File

@@ -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
@@ -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.
@@ -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

View File

@@ -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
@@ -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,

View File

@@ -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],

View File

@@ -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);

View File

@@ -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[];

View File

@@ -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;
default:
scalarOut[i] = scalarIn[31 - i];
}
}
}
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);
}
if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) {
THROW(0x6B00);
}
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);
}
#ifndef NO_CONSENT
if (p1 == P1_NON_CONFIRM)
#endif // NO_CONSENT
{
*tx = set_result_perform_privacy_operation();
THROW(0x9000);
}
#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
}

View File

@@ -0,0 +1,3 @@
#include "shared_context.h"
uint32_t set_result_perform_privacy_operation(void);

View File

@@ -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;
}

View File

@@ -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) {
uint32_t tx = set_result_perform_privacy_operation();
G_io_apdu_buffer[tx++] = 0x90;
G_io_apdu_buffer[tx++] = 0x00;
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;
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
}

View File

@@ -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);