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

Change derivation path for the keychain #35

Merged
merged 4 commits into from
May 31, 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 @@ -10,7 +10,7 @@ APP_LOAD_PARAMS=--appFlags 0x200 # APPLICATION_FLAG_BOLOS_SETTINGS
else
APP_LOAD_PARAMS=--appFlags 0x000
endif
APP_LOAD_PARAMS += --path "44'/650'"
APP_LOAD_PARAMS += --path "650'"
APP_LOAD_PARAMS += $(COMMON_LOAD_PARAMS)

APPNAME = "ARCHEthic"
Expand Down
10 changes: 5 additions & 5 deletions run.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash
#make clean
#make
#../speculos/speculos.py --seed hex:9A34A233C48B77D48856ED171792D0B167F50D9A8148769E000D1F3C75F74C15 bin/app.elf
../speculos/speculos.py --seed hex:6fa774718b0f086101e7a0bf43f81944f2eea0392bc3452ac314cc444f19978989c62be4110f8fd3e543875e9f3fe2e2240f554cf16cfebf673b112ac44ec016 bin/app.elf
#../speculos/speculos.py bin/app.elf
make clean
make
# ../speculos/speculos.py --seed hex:9A34A233C48B77D48856ED171792D0B167F50D9A8148769E000D1F3C75F74C15 bin/app.elf
../speculos/speculos.py --seed hex:6fa774718b0f086101e7a0bf43f81944f2eea0392bc3452ac314cc444f19978989c62be4110f8fd3e543875e9f3fe2e2240f554cf16cfebf673b112ac44ec016 --color JADE_GREEN bin/app.elf
# ../speculos/speculos.py bin/app.elf
216 changes: 177 additions & 39 deletions src/archethic.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ void io_exchange_with_code(uint16_t code, uint16_t tx)
{
G_io_apdu_buffer[tx++] = code >> 8;
G_io_apdu_buffer[tx++] = code & 0xFF;

io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, tx);
}

Expand All @@ -34,21 +35,32 @@ void getOriginPublicKey(cx_ecfp_public_key_t *publicKey)
void performECDH(uint8_t *ephPublicKey, uint8_t ephPublicKeySize, uint8_t *ecdhPointX)
{
cx_ecfp_private_key_t originPrivateKey;

deriveArchEthicKeyPair(CX_CURVE_SECP256K1, 650, 0xffff, 0, NULL, 0, &originPrivateKey, NULL);

cx_ecdh(&originPrivateKey, CX_ECDH_X, ephPublicKey, ephPublicKeySize, ecdhPointX, 32);
}

void decryptWallet(uint8_t *ecdhPointX, uint8_t ecdhPointLen, uint8_t *dataBuffer, uint8_t dataLen, uint8_t *encodedWallet, uint8_t *walletLen)
{

uint8_t aes_key_iv_tag[64] = {0};

// Double Hashing sha512 Here using ecdhPointX
cx_hash_sha512(ecdhPointX, ecdhPointLen, aes_key_iv_tag, sizeof(aes_key_iv_tag));

cx_hash_sha512(aes_key_iv_tag, sizeof(aes_key_iv_tag), aes_key_iv_tag, sizeof(aes_key_iv_tag));

uint8_t auth_key[32] = {0};
uint8_t auth_tag[32] = {0};
// Auth Key is sha256 hash of last 16 bytes of aes_key_iv_tag
cx_hash_sha256(aes_key_iv_tag + 48, 16, auth_key, sizeof(auth_key));

// Auth Tag is hmac_sha256 of dataBuffer
cx_hmac_sha256(auth_key, sizeof(auth_key), dataBuffer + 16, 32, auth_tag, sizeof(auth_tag));

uint8_t verify_tag = memcmp(auth_tag, dataBuffer, 16);
// If verify tag is non zero then it's a bad decode.

uint8_t wallet_key[32] = {0};
uint8_t wallet_iv[32] = {0};
Expand Down Expand Up @@ -78,35 +90,145 @@ void decryptWallet(uint8_t *ecdhPointX, uint8_t ecdhPointLen, uint8_t *dataBuffe
}
}

void getBIP44Path(uint32_t address_index, uint8_t *encoded_wallet, uint8_t wallet_len, uint8_t sequence_no, char *string_bip_44, uint8_t *bip44_len)
void getDerivationPath(uint8_t service_index, uint8_t *encoded_wallet, uint8_t wallet_len, uint8_t sequence_no, char *string_derivation_path, uint8_t *derivation_path_len, uint32_t *seek_bytes)
{
if (wallet_len < 5 * sequence_no + 39)
// Assuming minimum length for encoded onchain wallet be atleast 39 bytes
// Minimum bytes a service can have is 6 bytes
if (wallet_len < 6 * service_index + 39)
return;
// strncpy(string_bip_44, "m/44'/650'/ffff'/0'/0'", 60);
int coin_type = (encoded_wallet[5 * sequence_no + 34] << 8) | encoded_wallet[5 * sequence_no + 35];
int account = (encoded_wallet[5 * sequence_no + 36] << 8) | encoded_wallet[5 * sequence_no + 37];

strncpy(string_bip_44, "m/44'/", 7);
snprintf(string_bip_44 + 6, 6, "%d", coin_type);
// Read the encoded wallet in sequential manner here.
// Since paths and seed length can be varying
int seek_bytes_l = 0;

// First 4 bytes are version of onchain wallet;

seek_bytes_l += 4;

strncpy(string_bip_44 + strlen(string_bip_44), "'/", 3);
snprintf(string_bip_44 + strlen(string_bip_44), 6, "%d", account);
strncpy(string_bip_44 + strlen(string_bip_44), "'/0'/", 6);
// Read the seed length
uint8_t seed_length = encoded_wallet[seek_bytes_l];

format_u64(string_bip_44 + strlen(string_bip_44), 11, address_index);
strncpy(string_bip_44 + strlen(string_bip_44), "'", 2);
*bip44_len = strlen(string_bip_44);
seek_bytes_l += 1;

seek_bytes_l += seed_length;

// Now encoded wallet is pointing to total no. of services
uint8_t total_services = encoded_wallet[seek_bytes_l];

seek_bytes_l += 1;

// Requesting Service Index cannot be outside of total services in onchain wallet
if (total_services < service_index)
return;

for (int c = 0; c <= service_index; c++)
{
// For each service seek and get the derivation path, if not the required service index seek to the next one;
if (c == service_index)
{
// Get the values and break
int name_len = encoded_wallet[seek_bytes_l];
// encoded_wallet += name_len + 1;
seek_bytes_l += name_len + 1;

int path_len = encoded_wallet[seek_bytes_l];

// Derivation Path is in the form m/650'/0'/0'

// +1 in path len is required for null char in string
snprintf(string_derivation_path, path_len + 1, "%s", encoded_wallet + seek_bytes_l + 1);
*derivation_path_len = strlen(string_derivation_path);
break;
}
else
{
// Seek the encoded wallet to next service
int name_len = encoded_wallet[seek_bytes_l];
// encoded_wallet += name_len + 1;
seek_bytes_l += name_len + 1;
int path_len = encoded_wallet[seek_bytes_l];
// Extra two bytes are for curve type and hash type
// encoded_wallet += path_len + 1 + 1 + 1;
seek_bytes_l += path_len + 1 + 1 + 1;
}
}

*seek_bytes = seek_bytes_l;
}

void generateKeyFromWallet(uint32_t address_index, uint8_t *encoded_wallet, uint8_t *wallet_len, uint32_t sequence_no, uint8_t *curve_type, cx_ecfp_private_key_t *privateKey, cx_ecfp_public_key_t *publicKey)
void generateKeyFromWallet(uint32_t address_index_offset, uint8_t *encoded_wallet, uint8_t *wallet_len, uint8_t service_index, uint32_t seek_bytes, uint8_t *curve_type, cx_ecfp_private_key_t *privateKey, cx_ecfp_public_key_t *publicKey)
{
if (*wallet_len < 5 * sequence_no + 39)
if (*wallet_len < 6 * service_index + 39)
return;

// Parse the Derivation Path into components here
int path_len = encoded_wallet[seek_bytes];

char temp_der_path[30];
explicit_bzero(temp_der_path, sizeof(temp_der_path));

snprintf(temp_der_path, path_len + 1, "%s", encoded_wallet + seek_bytes + 1);

// Derivation path should start with m/..
if (temp_der_path[0] != 'm' || temp_der_path[1] != '/')
{
// Malformed Derivation path return
return;
uint32_t coin_type = (encoded_wallet[5 * sequence_no + 34] << 8) | encoded_wallet[5 * sequence_no + 35];
uint32_t account = (encoded_wallet[5 * sequence_no + 36] << 8) | encoded_wallet[5 * sequence_no + 37];
}

uint32_t coin_type = 0;
uint32_t account = 0;
uint32_t address_index = 0;

int dig_c = 0;

// ix will be 0 for scanning coin_type, 1 for scanning account, 2 for scanning address_index
uint8_t ix = 0;

for (int x = 2; x < path_len; ++x)
{
if (temp_der_path[x] == '/')
{
dig_c = 0;
ix += 2;
}
else
{
// Only process if char is a digit
if (temp_der_path[x] >= '0' && temp_der_path[x] <= '9')
{
if (ix == 0)
{
// Coin type
coin_type *= 10;
coin_type += (int)(temp_der_path[x] - '0');
}
else if (ix == 1)
{
// Account
account *= 10;
account += (int)(temp_der_path[x] - '0');
}
else if (ix == 2)
{
// Index
address_index *= 10;
account += (int)(temp_der_path[x] - '0');
}
dig_c++;
}
}
}

// PRINTF("\n Coin Type %d \n", coin_type);
// PRINTF("\n Account %d \n", account);
// PRINTF("\n Addr Index %d \n", address_index);

// Offset the index if any
address_index += address_index_offset;

cx_curve_t curve;
*curve_type = encoded_wallet[5 * sequence_no + 38];
*curve_type = encoded_wallet[seek_bytes + 1 + path_len];
switch (*curve_type)
{
case 0:
Expand All @@ -122,16 +244,20 @@ void generateKeyFromWallet(uint32_t address_index, uint8_t *encoded_wallet, uint
curve = CX_CURVE_SECP256K1;
break;
}
deriveArchEthicKeyPair(curve, coin_type, account, address_index, encoded_wallet + 1, 32, privateKey, publicKey);
// First 4 bytes are version
uint8_t seed_len = encoded_wallet[4];

deriveArchEthicKeyPair(curve, coin_type, account, address_index, encoded_wallet + 5, seed_len, privateKey, publicKey);
}

void generateArchEthicAddress(uint8_t hash_type, uint32_t address_index, uint8_t *encoded_wallet, uint8_t *wallet_len, uint32_t sequence_no, uint8_t *address, uint8_t *address_len)
void generateArchEthicAddress(uint8_t hash_type, uint8_t service_index, uint8_t *encoded_wallet, uint8_t *wallet_len, uint32_t sequence_no, uint8_t *address, uint8_t *address_len, uint32_t seek_bytes, uint32_t address_index_offset)
{

cx_ecfp_public_key_t publicKey;
publicKey.W_len = 0;
uint8_t curve_type = 255;
generateKeyFromWallet(address_index, encoded_wallet, wallet_len, sequence_no, &curve_type, NULL, &publicKey);

generateKeyFromWallet(address_index_offset, encoded_wallet, wallet_len, service_index, seek_bytes, &curve_type, NULL, &publicKey);

address[0] = curve_type;
address[1] = 0; // onchain wallet origin
Expand All @@ -153,7 +279,16 @@ void deriveArchEthicKeyPair(cx_curve_t curve, uint32_t coin_type, uint32_t accou
uint8_t rawPrivateKey[32];
cx_ecfp_private_key_t walletPrivateKey;

uint32_t bip44Path[] = {44 | 0x80000000, coin_type | 0x80000000, account | 0x80000000, 0x80000000, address_index | 0x80000000};
// uint32_t bip44Path[] = {44 | 0x80000000, coin_type | 0x80000000, account | 0x80000000, 0x80000000, address_index | 0x80000000};

// New Scheme for derivation path is
// m/650'/0'/0' where m/coin_type/account/address_index
uint32_t derivationPath[] = {coin_type | 0x80000000,
account | 0x80000000,
address_index | 0x80000000};

// If hashedPath is required, do it here
// Hashed Path is not required hence send raw paths.

int mode = -1;
if (curve == CX_CURVE_Ed25519)
Expand All @@ -163,9 +298,9 @@ void deriveArchEthicKeyPair(cx_curve_t curve, uint32_t coin_type, uint32_t accou

if (account == 0xffff)
// Derive the raw private key for the given path
os_perso_derive_node_with_seed_key(mode, curve, bip44Path, 5, rawPrivateKey, NULL, NULL, 0);
os_perso_derive_node_with_seed_key(mode, curve, derivationPath, 3, rawPrivateKey, NULL, NULL, 0);
else
archethic_derive_node_with_seed_key(mode, curve, masterSeed, masterSeedLen, bip44Path, 5, rawPrivateKey, NULL, NULL, 0);
archethic_derive_node_with_seed_key(mode, curve, masterSeed, masterSeedLen, derivationPath, 3, rawPrivateKey, NULL, NULL, 0);

// Initiate the private key structure with the raw private key
cx_ecfp_init_private_key(curve, rawPrivateKey, 32, &walletPrivateKey);
Expand All @@ -184,20 +319,21 @@ void deriveArchEthicKeyPair(cx_curve_t curve, uint32_t coin_type, uint32_t accou
explicit_bzero(&walletPrivateKey, sizeof(walletPrivateKey));
}

void performECDSA(uint8_t *txHash, uint8_t txHashLen, uint32_t address_index, uint8_t *encoded_wallet, uint8_t *wallet_len, uint8_t sequence_no, uint8_t *asn_sign, uint8_t *sign_len)
void performECDSA(uint8_t *txHash, uint8_t txHashLen, uint32_t address_index_offset, uint8_t *encoded_wallet, uint8_t *wallet_len, uint8_t service_index, uint32_t seek_bytes, uint8_t *asn_sign, uint8_t *sign_len)
{
uint8_t curve_type = 255;
cx_ecfp_private_key_t privateKey;
cx_ecfp_public_key_t publicKey;
publicKey.W_len = 0;
unsigned int info = 0;
generateKeyFromWallet(address_index, encoded_wallet, wallet_len, sequence_no, &curve_type, &privateKey, &publicKey);

generateKeyFromWallet(address_index_offset, encoded_wallet, wallet_len, service_index, seek_bytes, &curve_type, &privateKey, &publicKey);

asn_sign[0] = curve_type;
asn_sign[1] = 0; // Onchain Wallet Key
memcpy(asn_sign + 2, publicKey.W, publicKey.W_len);

*sign_len = cx_ecdsa_sign(&privateKey, CX_RND_TRNG, CX_SHA256, txHash, txHashLen, asn_sign + publicKey.W_len + 2, *sign_len - publicKey.W_len - 2, &info);
*sign_len = cx_ecdsa_sign(&privateKey, CX_RND_TRNG, CX_SHA256, txHash, txHashLen, asn_sign + publicKey.W_len + 2, MAX_ENCODE_WALLET_LEN - publicKey.W_len - 2, &info);
*sign_len += publicKey.W_len + 2;
}

Expand Down Expand Up @@ -227,8 +363,8 @@ void getTransactionHash(uint8_t *senderAddr, uint8_t senderAddrLen,
* After V1 Specs are implemented, remove
* +1 from senderAddr and -1 from senderAddrLen
*/
memcpy(tx + index, senderAddr + 1, senderAddrLen - 1);
index += senderAddrLen - 1;
memcpy(tx + index, senderAddr, senderAddrLen);
index += senderAddrLen;

memcpy(tx + index, &tx_type, 1);
index += 1;
Expand All @@ -251,8 +387,8 @@ void getTransactionHash(uint8_t *senderAddr, uint8_t senderAddrLen,
* After V1 Specs are implemented, remove
* +1 from receiveAddr and -1 from receiveAddrLen
*/
memcpy(tx + index, receiveAddr + 1, receiveAddrLen - 1);
index += receiveAddrLen - 1;
memcpy(tx + index, receiveAddr, receiveAddrLen);
index += receiveAddrLen;

memcpy(tx + index, amount, 8);
index += 8;
Expand All @@ -265,13 +401,15 @@ void getTransactionHash(uint8_t *senderAddr, uint8_t senderAddrLen,

*txHashLen = 32;
cx_hash_sha256(tx, index, txHash, *txHashLen);

// PRINTF("\n Encoded Txn: ---------- \n");
// for (int k = 0; k < index; k++)
// PRINTF("%02X", tx[k]);
// PRINTF("\n\n");

/*
for (int k = 0; k < index; k++)
PRINTF("%02X", tx[k]);
PRINTF("\n\n");

for (int k = 0; k < *txHashLen; k++)
PRINTF("%02X", txHash[k]);
PRINTF("\n\n");
*/
for (int k = 0; k < *txHashLen; k++)
PRINTF("%02X", txHash[k]);
PRINTF("\n\n");
*/
}
Loading