Skip to content

Commit 87fa920

Browse files
Merge pull request #240 from btchip/eip1024
Add EIP 1024 APDUs
2 parents 170f3ee + 52738e6 commit 87fa920

File tree

11 files changed

+300
-2
lines changed

11 files changed

+300
-2
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ APP_LOAD_PARAMS += --path "1517992542'/1101353413'"
3434

3535
APPVERSION_M=1
3636
APPVERSION_N=9
37-
APPVERSION_P=17
37+
APPVERSION_P=18
3838
APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)
3939
APP_LOAD_FLAGS= --appFlags 0x240 --dep Ethereum:$(APPVERSION)
4040

doc/ethapp.asc

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Ethereum application : Common Technical Specifications
22
=======================================================
33
Ledger Firmware Team <hello@ledger.fr>
4-
Application version 1.5.0 - 25th of September 2020
4+
Application version 1.9.18 - 29th of January 2022
55

66
## 1.0
77
- Initial release
@@ -26,6 +26,9 @@ Application version 1.5.0 - 25th of September 2020
2626
## 1.9.13
2727
- Add SET PLUGIN
2828

29+
## 1.9.17
30+
- Add PERFORM PRIVACY OPERATION
31+
2932
## About
3033

3134
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
379382

380383
None
381384

385+
### PERFORM PRIVACY OPERATION
386+
387+
#### Description
388+
389+
This command performs privacy operations as defined in EIP 1024 (https://ethereum-magicians.org/t/eip-1024-cross-client-encrypt-decrypt/505)
390+
391+
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
392+
393+
All data can be optionally checked on the device before being returned.
394+
395+
#### Coding
396+
397+
'Command'
398+
399+
[width="80%"]
400+
|==============================================================================================================================
401+
| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le*
402+
| E0 | 18 | 00 : return data
403+
404+
01 : display data and confirm before returning
405+
| 00 : return the public encryption key
406+
407+
01 : return the shared secret | variable | variable
408+
|==============================================================================================================================
409+
410+
'Input data'
411+
412+
[width="80%"]
413+
|==============================================================================================================================
414+
| *Description* | *Length*
415+
| Number of BIP 32 derivations to perform (max 10) | 1
416+
| First derivation index (big endian) | 4
417+
| ... | 4
418+
| Last derivation index (big endian) | 4
419+
| Third party public key on Curve25519, if returning the shared secret | 32
420+
|==============================================================================================================================
421+
422+
'Output data'
423+
424+
[width="80%"]
425+
|==============================================================================================================================
426+
| *Description* | *Length*
427+
| Public encryption key or shared secret | 32
428+
|==============================================================================================================================
429+
430+
382431
### GET APP CONFIGURATION
383432

384433
#### Description

src/apdu_constants.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#define INS_SET_EXTERNAL_PLUGIN 0x12
2121
#define INS_PROVIDE_NFT_INFORMATION 0x14
2222
#define INS_SET_PLUGIN 0x16
23+
#define INS_PERFORM_PRIVACY_OPERATION 0x18
2324
#define P1_CONFIRM 0x01
2425
#define P1_NON_CONFIRM 0x00
2526
#define P2_NO_CHAINCODE 0x00
@@ -114,6 +115,13 @@ void handleSetPlugin(uint8_t p1,
114115
unsigned int *flags,
115116
unsigned int *tx);
116117

118+
void handlePerformPrivacyOperation(uint8_t p1,
119+
uint8_t p2,
120+
uint8_t *workBuffer,
121+
uint16_t dataLength,
122+
unsigned int *flags,
123+
unsigned int *tx);
124+
117125
#ifdef HAVE_ETH2
118126

119127
void handleGetEth2PublicKey(uint8_t p1,

src/main.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,15 @@ void handleApdu(unsigned int *flags, unsigned int *tx) {
540540
tx);
541541
break;
542542

543+
case INS_PERFORM_PRIVACY_OPERATION:
544+
handlePerformPrivacyOperation(G_io_apdu_buffer[OFFSET_P1],
545+
G_io_apdu_buffer[OFFSET_P2],
546+
G_io_apdu_buffer + OFFSET_CDATA,
547+
G_io_apdu_buffer[OFFSET_LC],
548+
flags,
549+
tx);
550+
break;
551+
543552
case INS_SIGN:
544553
handleSign(G_io_apdu_buffer[OFFSET_P1],
545554
G_io_apdu_buffer[OFFSET_P2],

src/ui_callbacks.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ unsigned int io_seproxyhal_touch_data_cancel(const bagl_element_t *e);
1717
unsigned int io_seproxyhal_touch_signMessage712_v0_ok(const bagl_element_t *e);
1818
unsigned int io_seproxyhal_touch_signMessage712_v0_cancel(const bagl_element_t *e);
1919
unsigned int io_seproxyhal_touch_eth2_address_ok(const bagl_element_t *e);
20+
unsigned int io_seproxyhal_touch_privacy_ok(const bagl_element_t *e);
21+
unsigned int io_seproxyhal_touch_privacy_cancel(const bagl_element_t *e);
2022

2123
void ui_idle(void);
2224
void ui_warning_contract_data(void);

src/ui_flow.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ extern const ux_flow_step_t* const ux_sign_712_v0_flow[];
2626

2727
extern const ux_flow_step_t* const ux_display_public_eth2_flow[];
2828

29+
extern const ux_flow_step_t* const ux_display_privacy_public_key_flow[];
30+
31+
extern const ux_flow_step_t* const ux_display_privacy_shared_secret_flow[];
32+
2933
#ifdef HAVE_STARKWARE
3034

3135
extern const ux_flow_step_t* const ux_display_stark_public_flow[];
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#include "shared_context.h"
2+
#include "apdu_constants.h"
3+
4+
#include "ui_flow.h"
5+
#include "feature_performPrivacyOperation.h"
6+
7+
#define P2_PUBLIC_ENCRYPTION_KEY 0x00
8+
#define P2_SHARED_SECRET 0x01
9+
10+
void decodeScalar(const uint8_t *scalarIn, uint8_t *scalarOut) {
11+
for (uint8_t i = 0; i < 32; i++) {
12+
switch (i) {
13+
case 0:
14+
scalarOut[0] = (scalarIn[31] & 0x7f) | 0x40;
15+
break;
16+
case 31:
17+
scalarOut[31] = scalarIn[0] & 0xf8;
18+
break;
19+
default:
20+
scalarOut[i] = scalarIn[31 - i];
21+
}
22+
}
23+
}
24+
25+
void handlePerformPrivacyOperation(uint8_t p1,
26+
uint8_t p2,
27+
uint8_t *dataBuffer,
28+
uint16_t dataLength,
29+
unsigned int *flags,
30+
unsigned int *tx) {
31+
UNUSED(dataLength);
32+
uint8_t privateKeyData[INT256_LENGTH];
33+
uint8_t privateKeyDataSwapped[INT256_LENGTH];
34+
uint32_t bip32Path[MAX_BIP32_PATH];
35+
uint8_t bip32PathLength = *(dataBuffer++);
36+
cx_err_t status = CX_OK;
37+
if (p2 == P2_PUBLIC_ENCRYPTION_KEY) {
38+
if (dataLength < 1 + 4 * bip32PathLength) {
39+
THROW(0x6700);
40+
}
41+
} else if (p2 == P2_SHARED_SECRET) {
42+
if (dataLength < 1 + 4 * bip32PathLength + 32) {
43+
THROW(0x6700);
44+
}
45+
} else {
46+
THROW(0x6B00);
47+
}
48+
cx_ecfp_private_key_t privateKey;
49+
if ((bip32PathLength < 0x01) || (bip32PathLength > MAX_BIP32_PATH)) {
50+
PRINTF("Invalid path\n");
51+
THROW(0x6a80);
52+
}
53+
if ((p1 != P1_CONFIRM) && (p1 != P1_NON_CONFIRM)) {
54+
THROW(0x6B00);
55+
}
56+
for (uint8_t i = 0; i < bip32PathLength; i++) {
57+
bip32Path[i] = U4BE(dataBuffer, 0);
58+
dataBuffer += 4;
59+
}
60+
os_perso_derive_node_bip32(
61+
CX_CURVE_256K1,
62+
bip32Path,
63+
bip32PathLength,
64+
privateKeyData,
65+
(tmpCtx.publicKeyContext.getChaincode ? tmpCtx.publicKeyContext.chainCode : NULL));
66+
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);
67+
cx_ecfp_generate_pair(CX_CURVE_256K1, &tmpCtx.publicKeyContext.publicKey, &privateKey, 1);
68+
getEthAddressStringFromKey(&tmpCtx.publicKeyContext.publicKey,
69+
tmpCtx.publicKeyContext.address,
70+
&global_sha3,
71+
chainConfig->chainId);
72+
if (p2 == P2_PUBLIC_ENCRYPTION_KEY) {
73+
decodeScalar(privateKeyData, privateKeyDataSwapped);
74+
cx_ecfp_init_private_key(CX_CURVE_Curve25519, privateKeyDataSwapped, 32, &privateKey);
75+
cx_ecfp_generate_pair(CX_CURVE_Curve25519,
76+
&tmpCtx.publicKeyContext.publicKey,
77+
&privateKey,
78+
1);
79+
explicit_bzero(privateKeyDataSwapped, sizeof(privateKeyDataSwapped));
80+
} else {
81+
memmove(tmpCtx.publicKeyContext.publicKey.W + 1, dataBuffer, 32);
82+
status = cx_x25519(tmpCtx.publicKeyContext.publicKey.W + 1, privateKeyData, 32);
83+
}
84+
explicit_bzero(&privateKey, sizeof(privateKey));
85+
explicit_bzero(privateKeyData, sizeof(privateKeyData));
86+
87+
if (status != CX_OK) {
88+
THROW(0x6A80);
89+
}
90+
91+
#ifndef NO_CONSENT
92+
if (p1 == P1_NON_CONFIRM)
93+
#endif // NO_CONSENT
94+
{
95+
*tx = set_result_perform_privacy_operation();
96+
THROW(0x9000);
97+
}
98+
#ifndef NO_CONSENT
99+
else {
100+
snprintf(strings.common.fullAddress,
101+
sizeof(strings.common.fullAddress),
102+
"0x%.*s",
103+
40,
104+
tmpCtx.publicKeyContext.address);
105+
for (uint8_t i = 0; i < 32; i++) {
106+
privateKeyData[i] = tmpCtx.publicKeyContext.publicKey.W[32 - i];
107+
}
108+
snprintf(strings.common.fullAmount,
109+
sizeof(strings.common.fullAmount) - 1,
110+
"%.*H",
111+
32,
112+
privateKeyData);
113+
if (p2 == P2_PUBLIC_ENCRYPTION_KEY) {
114+
ux_flow_init(0, ux_display_privacy_public_key_flow, NULL);
115+
} else {
116+
ux_flow_init(0, ux_display_privacy_shared_secret_flow, NULL);
117+
}
118+
119+
*flags |= IO_ASYNCH_REPLY;
120+
}
121+
#endif // NO_CONSENT
122+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include "shared_context.h"
2+
3+
uint32_t set_result_perform_privacy_operation(void);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include "shared_context.h"
2+
3+
uint32_t set_result_perform_privacy_operation() {
4+
for (uint8_t i = 0; i < 32; i++) {
5+
G_io_apdu_buffer[i] = tmpCtx.publicKeyContext.publicKey.W[32 - i];
6+
}
7+
return 32;
8+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include "shared_context.h"
2+
#include "feature_getPublicKey.h"
3+
#include "ui_callbacks.h"
4+
5+
unsigned int io_seproxyhal_touch_privacy_ok(__attribute__((unused)) const bagl_element_t *e) {
6+
uint32_t tx = set_result_perform_privacy_operation();
7+
G_io_apdu_buffer[tx++] = 0x90;
8+
G_io_apdu_buffer[tx++] = 0x00;
9+
reset_app_context();
10+
// Send back the response, do not restart the event loop
11+
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx);
12+
// Display back the original UX
13+
ui_idle();
14+
return 0; // do not redraw the widget
15+
}
16+
17+
unsigned int io_seproxyhal_touch_privacy_cancel(__attribute__((unused)) const bagl_element_t *e) {
18+
G_io_apdu_buffer[0] = 0x69;
19+
G_io_apdu_buffer[1] = 0x85;
20+
reset_app_context();
21+
// Send back the response, do not restart the event loop
22+
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
23+
// Display back the original UX
24+
ui_idle();
25+
return 0; // do not redraw the widget
26+
}

0 commit comments

Comments
 (0)