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

Feat/ldg 506 nano app implement verifyaddress method #5

Merged
merged 14 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
14 changes: 13 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
{
"files.associations": {
"*.h": "c"
"*.h": "c",
"__hash_table": "c",
"__split_buffer": "c",
"array": "c",
"bitset": "c",
"deque": "c",
"initializer_list": "c",
"queue": "c",
"stack": "c",
"string": "c",
"string_view": "c",
"unordered_map": "c",
"vector": "c"
},
"C_Cpp.clang_format_path": "/usr/bin/clang-format",
"editor.formatOnSave": false,
Expand Down
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ VARIANT_PARAM = COIN
VARIANT_VALUES = CCD

# Enabling DEBUG flag will enable PRINTF and disable optimizations
#DEBUG = 1
DEBUG = 1

ifeq ($(DEBUG),1)
# debugging helper functions and macros
CFLAGS += -g
endif

########################################
# Application custom permissions #
Expand Down
17 changes: 17 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# version: "3.7"

services:
nanosp:
image: ghcr.io/blooo-io/speculos:latest-aarch64
volumes:
- ./bin:/speculos/apps
- ./src:/speculos/sources
- ./lib-app-bitcoin:/speculos/lib
- ./build:/speculos/build
ports:
- "5000:5000" # api
- "40000:40000" # apdu
environment:
- GDB_DIRECTORY_LIST="/speculos/sources:/speculos/sources/apdu:/speculos/sources/handler:/speculos/sources/helper:/speculos/sources/transaction:/speculos/sources/ui"
command: "-d --model nanox build/nanox/bin/app.elf --display headless --apdu-port 40000 "
# Add `--vnc-password "<password>"` for macos users to use built-in vnc client.
12 changes: 12 additions & 0 deletions gdb.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

docker-compose up -d

# Wait for the container to start
until docker inspect -f '{{.State.Status}}' concordium-ledger-app-nanosp-1 | grep -q "running"; do
sleep 1
done

docker exec -it concordium-ledger-app-nanosp-1 ./tools/debug.sh apps/app.elf

docker-compose down
16 changes: 16 additions & 0 deletions src/apdu/dispatcher.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "../types.h"
#include "../sw.h"
#include "../handler/get_version.h"
#include "../handler/verify_address.h"
#include "../handler/get_app_name.h"
#include "../handler/get_public_key.h"
#include "../handler/sign_tx.h"
Expand All @@ -54,6 +55,21 @@ int apdu_dispatcher(const command_t *cmd) {
}

return handler_get_app_name();

case VERIFY_ADDRESS:
// p1 can be 0 or 1:
//0 for the legacy address format, 1 for the new address format
if ((cmd->p1 != 0 && cmd->p1 != 1) || cmd->p2 != 0) {
return io_send_sw(SW_WRONG_P1P2);
}
if (!cmd->data) {
return io_send_sw(SW_WRONG_DATA_LENGTH);
}
buf.ptr = cmd->data;
buf.size = cmd->lc;
buf.offset = 0;
return handler_verify_address(&buf, (bool) cmd->p1);

case GET_PUBLIC_KEY:
if (cmd->p1 > 1 || cmd->p2 > 0) {
return io_send_sw(SW_WRONG_P1P2);
Expand Down
69 changes: 69 additions & 0 deletions src/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,72 @@
* Exponent used to convert mCCD to CCD unit (N CCD = N * 10^3 mCCD).
*/
#define EXPONENT_SMALLEST_UNIT 3

/**
* Hardened offset for BIP32 path.
*/
#define HARDENED_OFFSET 0x80000000

/**
* BIP44 purpose value for legacy derivation paths.
*/
#define LEGACY_PURPOSE 1105

/**
* Coin type value for Concordium in legacy derivation paths.
*/
#define LEGACY_COIN_TYPE 0

/**
* Account subtree index for legacy derivation paths.
*/
#define LEGACY_ACCOUNT_SUBTREE 0

/**
* Normal account index used in legacy derivation paths.
*/
#define LEGACY_NORMAL_ACCOUNT 0

/**
* Legacy key indexes used in derivation paths.
*/
enum legacy_key_indexes {
LEGACY_ID_CRED = 0,
LEGACY_PRF_KEY = 1,
LEGACY_SIGN_KEY = 2,
LEGACY_R = 3,
LEGACY_M0 = 4
};

/**
* Purpose value for new address format.
*/
#define NEW_PURPOSE 44

/**
* Coin type value for new address format.
*/
#define NEW_COIN_TYPE 919

enum new_key_indexes {
NEW_SIGN_KEY = 0,
NEW_ID_CRED = 1,
NEW_PRF_KEY = 2,
NEW_M0 = 3,
NEW_R = 4
};

/**
* Length of the credential ID.
*/
#define CREDENTIAL_ID_LEN 48

/**
* Length of the address.
*/
#define CONCORDIUM_ADDRESS_LEN 32

/**
* Concordium version byte.
*/
#define CONCORDIUM_VERSION_BYTE 1
177 changes: 177 additions & 0 deletions src/handler/verify_address.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#include "verify_address.h"
#include <cx.h>
#include <os.h>

#include "../globals.h"
#include "../types.h"
#include "../sw.h"
#include "../helper/util.h"
#include "../constants.h"
#include "../ui/display.h" // For ui_display_verify_address


// gX and gY are the coordinates of g, which is the first part of the onchainCommitmentKey.
static const uint8_t gX[48] = {0x11, 0x4c, 0xbf, 0xe4, 0x4a, 0x02, 0xc6, 0xb1, 0xf7, 0x87, 0x11, 0x17,
0x6d, 0x5f, 0x43, 0x72, 0x95, 0x36, 0x7a, 0xa4, 0xf2, 0xa8, 0xc2, 0x55,
0x1e, 0xe1, 0x0d, 0x25, 0xa0, 0x3a, 0xdc, 0x69, 0xd6, 0x1a, 0x33, 0x2a,
0x05, 0x89, 0x71, 0x91, 0x9d, 0xad, 0x73, 0x12, 0xe1, 0xfc, 0x94, 0xc5};
static const uint8_t gY[48] = {0x18, 0x6a, 0xf3, 0x21, 0x19, 0x54, 0x39, 0x13, 0xb2, 0x6a, 0x46, 0x2a,
0x02, 0x31, 0xe4, 0xbf, 0x5f, 0xde, 0xe0, 0xb5, 0x2c, 0x91, 0x6f, 0x68,
0x85, 0x44, 0x87, 0xe8, 0x11, 0x2c, 0x1f, 0x27, 0x74, 0x35, 0xfc, 0x07,
0x6f, 0x3a, 0xda, 0xd5, 0x6d, 0x18, 0xd8, 0x6a, 0x65, 0x99, 0xb5, 0x42};


// The methods below are coded to work for the legacy address format.

cx_err_t get_credential_id(uint8_t *prf_key, size_t prf_key_len, uint32_t credential_counter, uint8_t *credential_id, size_t credential_id_len) {
cx_err_t error = 0;


// get bn lock to allow working with binary numbers and elliptic curves
CX_CHECK(cx_bn_lock(16, 0));
// Initialize binary numbers
cx_bn_t credIdExponentBn, tmpBn, rBn, ccBn, prfBn;
CX_CHECK(cx_bn_alloc(&credIdExponentBn, 32));
CX_CHECK(cx_bn_alloc(&tmpBn, 32));
CX_CHECK(cx_bn_alloc_init(&prfBn, 32, prf_key, prf_key_len));
CX_CHECK(cx_bn_alloc_init(&rBn, 32, r, sizeof(r)));
CX_CHECK(cx_bn_alloc(&ccBn, 32));
CX_CHECK(cx_bn_set_u32(ccBn, credential_counter));

// Apply cred counter offset
CX_CHECK(cx_bn_mod_add(tmpBn, prfBn, ccBn, rBn));

// Inverse of (prf + cred_counter) is the exponent for calculating the credId
CX_CHECK(cx_bn_mod_invert_nprime(credIdExponentBn, tmpBn, rBn));

// clean up binary numbers
CX_CHECK(cx_bn_destroy(&tmpBn));
CX_CHECK(cx_bn_destroy(&rBn));
CX_CHECK(cx_bn_destroy(&prfBn));
CX_CHECK(cx_bn_destroy(&ccBn));

// initialize elliptic curve point given by global commitmentKey
cx_ecpoint_t commitmentKey;
CX_CHECK(cx_ecpoint_alloc(&commitmentKey, CX_CURVE_BLS12_381_G1));
CX_CHECK(cx_ecpoint_init(&commitmentKey, gX, sizeof(gX), gY, sizeof(gY)));

// multiply commitmentKey with credIdExponent
CX_CHECK(cx_ecpoint_scalarmul_bn(&commitmentKey, credIdExponentBn));
CX_CHECK(cx_bn_destroy(&credIdExponentBn));

// calculate credId which is the compressed version of commitmentKey * credIdExponent
cx_bn_t x, y, negy;
CX_CHECK(cx_bn_alloc(&x, 48));
CX_CHECK(cx_bn_alloc(&y, 48));
CX_CHECK(cx_bn_alloc(&negy, 48));

CX_CHECK(cx_ecpoint_export_bn(&commitmentKey, &x, &y));
CX_CHECK(cx_bn_export(x, credential_id, credential_id_len));

// Calculate negation of the point to get -y
CX_CHECK(cx_ecpoint_neg(&commitmentKey));
CX_CHECK(cx_ecpoint_export_bn(&commitmentKey, &x, &negy));

int diff;
CX_CHECK(cx_bn_cmp(y, negy, &diff));

// cleanup binary numbers
CX_CHECK(cx_bn_destroy(&x));
CX_CHECK(cx_bn_destroy(&y));
CX_CHECK(cx_bn_destroy(&negy));
CX_CHECK(cx_ecpoint_destroy(&commitmentKey));

credential_id[0] |= 0x80; // Indicate this is on compressed form
if (diff > 0) {
credential_id[0] |= 0x20; // Indicate that y > -y
}

// CX_CHECK label to goto in case of an error
end:
if(error != CX_OK) {
PRINTF("something went wrong\n");
keiff3r marked this conversation as resolved.
Show resolved Hide resolved
}
cx_bn_unlock();
return error;
}

int handler_verify_address(buffer_t *cdata, bool is_new_address) {
// Reset context
explicit_bzero(&G_context, sizeof(G_context));
G_context.req_type = CONFIRM_ADDRESS;
G_context.state = STATE_NONE;

if(cdata->size != 8 && cdata->size != 12) {
return io_send_sw(SW_WRONG_DATA_LENGTH);
}
// Set the idp index to 0xffffffff
G_context.verify_address_info.idp_index = 0xffffffff;
// Read the idp index if it is a new address
if(is_new_address) {
if(!buffer_read_u32(cdata, &G_context.verify_address_info.idp_index, BE)) {
return io_send_sw(SW_WRONG_DATA_LENGTH);
}
}

// Read the identity index and credential counter
if (
!buffer_read_u32(cdata, &G_context.verify_address_info.identity_index, BE) ||
!buffer_read_u32(cdata, &G_context.verify_address_info.credential_counter, BE)) {
return io_send_sw(SW_WRONG_DATA_LENGTH);
}

size_t prf_key_path_len = is_new_address ? 5 : 6;
uint32_t *prf_key_path;
if(is_new_address) {
prf_key_path = (uint32_t[5]) {
NEW_PURPOSE | HARDENED_OFFSET,
NEW_COIN_TYPE | HARDENED_OFFSET,
G_context.verify_address_info.idp_index | HARDENED_OFFSET,
G_context.verify_address_info.identity_index | HARDENED_OFFSET,
NEW_PRF_KEY | HARDENED_OFFSET
};
}
else{
prf_key_path = (uint32_t[6]) {
LEGACY_PURPOSE | HARDENED_OFFSET,
LEGACY_COIN_TYPE | HARDENED_OFFSET,
LEGACY_ACCOUNT_SUBTREE | HARDENED_OFFSET,
LEGACY_NORMAL_ACCOUNT | HARDENED_OFFSET,
G_context.verify_address_info.identity_index | HARDENED_OFFSET,
LEGACY_PRF_KEY | HARDENED_OFFSET
};
}


uint8_t credential_id[CREDENTIAL_ID_LEN];
uint8_t prf_key[32];

if(get_bls_private_key(prf_key_path, prf_key_path_len, prf_key, sizeof(prf_key)) == -1) {
return io_send_sw(SW_VERIFY_ADDRESS_FAIL);
}

if(get_credential_id(prf_key,
sizeof(prf_key),
G_context.verify_address_info.credential_counter,
credential_id,
sizeof(credential_id)) != CX_OK) {
return io_send_sw(SW_VERIFY_ADDRESS_FAIL) ;
}

// Empty prf_key
explicit_bzero(prf_key, sizeof(prf_key));

uint8_t account_address[32];
cx_hash_sha256(credential_id, sizeof(credential_id), account_address, sizeof(account_address));

size_t address_len = sizeof(G_context.verify_address_info.address);

// This function will return the number of bytes encoded, or -1 on error.
int rtn = address_to_base58(account_address, sizeof(account_address), G_context.verify_address_info.address, address_len);
if(rtn < 0) {
return io_send_sw(SW_VERIFY_ADDRESS_FAIL);
}

return ui_display_verify_address();
Fixed Show fixed Hide fixed

}
25 changes: 25 additions & 0 deletions src/handler/verify_address.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include "buffer.h"
#include "cx.h"

/**
* Handler for VERIFY_ADDRESS command. Computes the address from
* identity index and credential counter and displays it on the screen.
*
* The user has to confirm the address on the screen matches the one
* from the dApp.
*
* @param[in,out] cdata
* Command data with identity index and credential counter.
* @param[in] is_new_address: true if the new address format is used, false for the legacy address format.
*
* @return zero or positive integer if success, negative integer otherwise.
*/
int handler_verify_address(buffer_t *cdata, bool is_new_address);

/**
* Calculates the credential ID from the given prf key and credential counter.
* The size of the computed credential ID is 48 bytes.
*/
cx_err_t get_credential_id(uint8_t *prf_key, size_t prf_key_len, uint32_t credential_counter, uint8_t *credential_id, size_t credential_id_len);
Loading
Loading