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

Support for mainnet/testnet address on any derivation #55

Merged
merged 9 commits into from
Mar 4, 2021
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
5 changes: 5 additions & 0 deletions app/src/apdu_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ __Z_INLINE void handleGetAddrSecp256K1(volatile uint32_t *flags, volatile uint32
extractHDPath(rx, OFFSET_DATA);

uint8_t requireConfirmation = G_io_apdu_buffer[OFFSET_P1];
uint8_t network = G_io_apdu_buffer[OFFSET_P2];

// Set the address version
if (!set_network_version(network))
return THROW(APDU_CODE_DATA_INVALID);

if (requireConfirmation) {
app_fill_address(addr_secp256k1);
Expand Down
31 changes: 27 additions & 4 deletions app/src/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,31 @@ typedef struct {
uint8_t hash_ripe[CX_RIPEMD160_SIZE];
} __attribute__((packed)) address_temp_t;


bool is_valid_network_version(uint8_t version);

// Set the network version to be used when getting the address from
// the device public key.
bool set_network_version(uint8_t network) {
if (is_valid_network_version(network)) {
version = network;
return true;
}
zemu_log_stack("Address version not supported/0");
return false;
}

bool is_valid_network_version(uint8_t version) {
switch(version) {
case COIN_VERSION_TESTNET_SINGLESIG: break;
case COIN_VERSION_MAINNET_SINGLESIG: break;
default: {
return false;
}
}
return true;
}

uint16_t crypto_fillAddress_secp256k1(uint8_t *buffer, uint16_t buffer_len) {
if (buffer_len < sizeof(answer_t)) {
return 0;
Expand All @@ -62,11 +87,9 @@ uint16_t crypto_fillAddress_secp256k1(uint8_t *buffer, uint16_t buffer_len) {

crypto_extractPublicKeyHash(address_temp.hash_ripe, CX_RIPEMD160_SIZE);

uint8_t version = COIN_VERSION_MAINNET_SINGLESIG;
if (isTestnet()) {
version = COIN_VERSION_MAINNET_SINGLESIG;
}
size_t outLen = sizeof_field(answer_t, address);
if ( !is_valid_network_version(version) )
version = COIN_VERSION_MAINNET_SINGLESIG;
neithanmo marked this conversation as resolved.
Show resolved Hide resolved
outLen = rs_c32_address(address_temp.hash_ripe, version, answer->address, outLen);

return PK_LEN_SECP256K1 + outLen;
Expand Down
4 changes: 4 additions & 0 deletions app/src/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ extern uint32_t hdPath[HDPATH_LEN_DEFAULT];

extern address_kind_e addressKind;

uint8_t version;

bool set_network_version(uint8_t version);

bool isTestnet();

void crypto_extractPublicKey(const uint32_t path[HDPATH_LEN_DEFAULT], uint8_t *pubKey, uint16_t pubKeyLen);
Expand Down
7 changes: 1 addition & 6 deletions deps/ledger-zxlib/dockerized_build.mk
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,7 @@ $(info EXAMPLE_VUE_DIR : $(EXAMPLE_VUE_DIR))
$(info TESTS_JS_DIR : $(TESTS_JS_DIR))
$(info TESTS_JS_PACKAGE : $(TESTS_JS_PACKAGE))

ifeq ($(USERID),1001)
# TODO: Use podman inside circleci machines?
DOCKER_IMAGE=zondax/builder-bolos-1001@sha256:423348672bb9f1e6aca573de29afa6763bcbead1a592cedb62c8fbfd82fb7f65
else
DOCKER_IMAGE=zondax/builder-bolos@sha256:2ce8f16b1e3face5464c538198e57a64340f664d932b3383d019f2636321f342
endif
DOCKER_IMAGE=zondax/builder-bolos@sha256:0e0097c01ef3c6c964fea8d8b6e3a584f4ff209a04ec68b6b2b4aeab64379c23

ifdef INTERACTIVE
INTERACTIVE_SETTING:="-i"
Expand Down
3 changes: 2 additions & 1 deletion js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"dependencies": {
"@babel/runtime": "^7.12.5",
"@ledgerhq/hw-transport": "^5.39.1",
"@types/ledgerhq__hw-transport": "^4.21.3"
"@types/ledgerhq__hw-transport": "^4.21.3",
"@stacks/transactions": "^1.0.1"
},
"devDependencies": {
"@babel/cli": "^7.12.10",
Expand Down
13 changes: 7 additions & 6 deletions js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
******************************************************************************* */
import Transport from '@ledgerhq/hw-transport';
import {serializePath} from './helper';
import {ResponseAddress, ResponseAppInfo, ResponseSign, ResponseVersion} from './types';
import {ResponseBase, ResponseAddress, ResponseAppInfo, ResponseSign, ResponseVersion} from './types';
import {
CHUNK_SIZE,
CLA,
Expand All @@ -30,6 +30,8 @@ import {
processErrorResponse,
} from './common';

import type { AddressVersion } from '@stacks/transactions';

export {LedgerError};
export * from './types';

Expand Down Expand Up @@ -139,18 +141,17 @@ export default class BlockstackApp {
}, processErrorResponse);
}

async getAddressAndPubKey(path: string): Promise<ResponseAddress> {
async getAddressAndPubKey(path: string, version: AddressVersion): Promise<ResponseAddress> {
const serializedPath = serializePath(path);
return this.transport
.send(CLA, INS.GET_ADDR_SECP256K1, P1_VALUES.ONLY_RETRIEVE, 0, serializedPath, [0x9000])
.send(CLA, INS.GET_ADDR_SECP256K1, P1_VALUES.ONLY_RETRIEVE, version, serializedPath, [0x9000])
.then(processGetAddrResponse, processErrorResponse);
}

async showAddressAndPubKey(path: string): Promise<ResponseAddress> {
async showAddressAndPubKey(path: string, version: AddressVersion): Promise<ResponseAddress> {
const serializedPath = serializePath(path);

return this.transport
.send(CLA, INS.GET_ADDR_SECP256K1, P1_VALUES.SHOW_ADDRESS_IN_DEVICE, 0, serializedPath, [
.send(CLA, INS.GET_ADDR_SECP256K1, P1_VALUES.SHOW_ADDRESS_IN_DEVICE, version, serializedPath, [
LedgerError.NoErrors,
])
.then(processGetAddrResponse, processErrorResponse);
Expand Down
2 changes: 1 addition & 1 deletion rust-toolchain
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.43.0
1.49.0
2 changes: 1 addition & 1 deletion tests_zemu/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"dependencies": {
"@stacks/network": "^1.0.1",
"@stacks/transactions": "^1.0.1",
"@zondax/ledger-blockstack": "^0.1.0",
"@zondax/ledger-blockstack": "file:../js",
"@zondax/zemu": "^0.11.0",
"bn.js": "^5.1.3"
},
Expand Down
20 changes: 14 additions & 6 deletions tests_zemu/tests/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import jest, { expect } from "jest";
import Zemu from "@zondax/zemu";
import NetworkVersion from "@zondax/ledger-blockstack";
import BlockstackApp from "@zondax/ledger-blockstack";
import {
broadcastTransaction,
Expand All @@ -24,7 +25,8 @@ import {
makeSTXTokenTransfer,
makeUnsignedSTXTokenTransfer,
pubKeyfromPrivKey,
publicKeyToString
publicKeyToString,
AddressVersion
} from "@stacks/transactions";
import { StacksTestnet } from "@stacks/network";
import { ec as EC } from "elliptic";
Expand Down Expand Up @@ -88,7 +90,7 @@ describe("Basic checks", function() {
await sim.start({ model: nanoModel.model, ...simOptions});
const app = new BlockstackApp(sim.getTransport());

const response = await app.getAddressAndPubKey("m/44'/5757'/5'/0/0", true);
const response = await app.getAddressAndPubKey("m/44'/5757'/5'/0/0", AddressVersion.MainnetSingleSig, true);
console.log(response);
expect(response.returnCode).toEqual(0x9000);

Expand All @@ -111,11 +113,11 @@ describe("Basic checks", function() {
// Derivation path. First 3 items are automatically hardened!
const path = "m/44'/5757'/5'/0/3";

const respRequest = app.showAddressAndPubKey(path);
const respRequest = app.showAddressAndPubKey(path, AddressVersion.MainnetSingleSig);
// Wait until we are not in the main menu
await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot());

await sim.compareSnapshotsAndAccept(".", `${nanoModel.prefix.toLowerCase()}-show-address`, nanoModel.model === 'nanos' ? 3 : 3);
await sim.compareSnapshotsAndAccept(".", `${nanoModel.prefix.toLowerCase()}-show-address`, nanoModel.model === 'nanos' ? 2 : 2);

const resp = await respRequest;
console.log(resp);
Expand All @@ -127,7 +129,13 @@ describe("Basic checks", function() {
const expected_publicKey = "02beafa347af54948b214106b9972cc4a05a771a2573f32905c48e4dc697171e60";

expect(resp.address).toEqual(expected_address_string);
console.log("Response address ", resp.address)
expect(resp.publicKey.toString("hex")).toEqual(expected_publicKey);


const response_t = await app.getAddressAndPubKey(path, AddressVersion.TestnetSingleSig);
const expected_testnet_address_string = "STGZNGF9PTR3ZPJN9J67WRYV5PSV783JY9ZMT3Y6";
expect(response_t.address).toEqual(expected_testnet_address_string);
} finally {
await sim.close();
}
Expand All @@ -144,7 +152,7 @@ describe("Basic checks", function() {
const app = new BlockstackApp(sim.getTransport());

// Get pubkey and check
const pkResponse = await app.getAddressAndPubKey(path);
const pkResponse = await app.getAddressAndPubKey(path, AddressVersion.TestnetSingleSig);
console.log(pkResponse);
expect(pkResponse.returnCode).toEqual(0x9000);
expect(pkResponse.errorMessage).toEqual("No errors");
Expand Down Expand Up @@ -196,7 +204,7 @@ describe("Basic checks", function() {
// Wait until we are not in the main men
await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot());

await sim.compareSnapshotsAndAccept(".", `${nanoModel.prefix.toLowerCase()}-signatureTest`, nanoModel.model === 'nanos' ? 9 : 8);
await sim.compareSnapshotsAndAccept(".", `${nanoModel.prefix.toLowerCase()}-signatureTest`, nanoModel.model === 'nanos' ? 8 : 7);

let signature = await signatureRequest;
console.log(signature);
Expand Down
Loading