Skip to content

Commit

Permalink
add getRandomness command
Browse files Browse the repository at this point in the history
  • Loading branch information
chcmedeiros committed May 6, 2024
1 parent 0db8922 commit c6de873
Show file tree
Hide file tree
Showing 10 changed files with 335 additions and 137 deletions.
41 changes: 23 additions & 18 deletions app/src/apdu_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,25 +199,31 @@ __Z_INLINE void handleGetKeys(volatile uint32_t *flags, volatile uint32_t *tx, u
THROW(APDU_CODE_OK);
}

__Z_INLINE void handleExtractSpendSignature(volatile uint32_t *tx,
uint32_t rx) {

*tx = 0;
if (rx != APDU_MIN_LENGTH || G_io_apdu_buffer[OFFSET_DATA_LEN] != 0) {
THROW(APDU_CODE_COMMAND_NOT_ALLOWED);
__Z_INLINE void handleComputeMaspRand(__Z_UNUSED volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) {
ZEMU_LOGF(50, "handleComputeMaspRand\n")
if (!process_chunk(tx, rx)) {
THROW(APDU_CODE_OK);
}
ZEMU_LOGF(50, "handleComputeMaspRand 2\n")
CHECK_APP_CANARY()

zxerr_t zxerr = crypto_extract_spend_signature(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 2);

if (zxerr != zxerr_ok) {
*tx = 0;
uint16_t replyLen = 0;
const uint8_t *message = tx_get_buffer();
const uint16_t messageLength = tx_get_buffer_length();

zxerr_t err = crypto_computeRandomness(message, messageLength, G_io_apdu_buffer, IO_APDU_BUFFER_SIZE - 3, &replyLen);
if (err != zxerr_ok) {
MEMZERO(G_io_apdu_buffer, IO_APDU_BUFFER_SIZE);
G_io_apdu_buffer[0] = err;
*tx = 0;
THROW(APDU_CODE_DATA_INVALID);
}
}

*tx = cmdResponseLen;
*tx = replyLen;
THROW(APDU_CODE_OK);
}

}

__Z_INLINE void handle_getversion(__Z_UNUSED volatile uint32_t *flags, volatile uint32_t *tx)
{
Expand Down Expand Up @@ -293,20 +299,19 @@ void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) {
break;
}

case INS_SIGN_MASP: {
case INS_INIT_MASP_TX: {
CHECK_PIN_VALIDATED()
handleSignMasp(flags, tx, rx);
handleComputeMaspRand(flags, tx, rx);
break;
}

case INS_EXTRACT_SPEND_SIGNATURE: {
case INS_SIGN_MASP: {
CHECK_PIN_VALIDATED()
handleExtractSpendSignature(tx, rx);
handleSignMasp(flags, tx, rx);
break;
}

#if defined(APP_TESTING)
case INS_TEST: {
case INS_TEST: {
handleTest(flags, tx, rx);
THROW(APDU_CODE_OK);
break;
Expand Down
4 changes: 2 additions & 2 deletions app/src/coin.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ typedef enum {
} key_kind_e;

#define INS_GET_KEYS 0x03
#define INS_SIGN_MASP 0x04
#define INS_EXTRACT_SPEND_SIGNATURE 0x05
#define INS_INIT_MASP_TX 0x04
#define INS_SIGN_MASP 0x05

#define APDU_CODE_CHECK_SIGN_TR_FAIL 0x6999
#ifdef __cplusplus
Expand Down
49 changes: 49 additions & 0 deletions app/src/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -774,3 +774,52 @@ zxerr_t crypto_sign_masp(const parser_tx_t *txObj, uint8_t *output, uint16_t out

return zxerr_ok;
}

static zxerr_t random_fr(uint8_t *buffer, uint16_t bufferLen) {
if (buffer == NULL || bufferLen < 32) {
return zxerr_unknown;
}

uint8_t rnd_data[64] = {0};
cx_trng_get_random_data(rnd_data, 64);
CHECK_PARSER_OK(from_bytes_wide(rnd_data, buffer));

return zxerr_ok;
}

zxerr_t crypto_computeRandomness(const uint8_t *buffer, uint16_t bufferLen, uint8_t *out, uint16_t outLen, uint16_t *replyLen) {
if(buffer == NULL || bufferLen != 3 || out == NULL ||replyLen == NULL) {
return zxerr_unknown;
}
MEMZERO(out, outLen);
zemu_log_stack("crypto_computeRandomness");
uint8_t spend_len = buffer[0];
uint8_t output_len = buffer[1];
uint8_t convert_len = buffer[2];
uint8_t tmp_rnd[32] = {0};
ZEMU_LOGF(50,"spend_len: %d, output_len: %d, convert_len: %d\n", spend_len, output_len, convert_len);
//value commitment randomness + spend auth randomizer
for (uint8_t i = 0; i < 2 * spend_len; i++) {
CHECK_ZXERR(random_fr(tmp_rnd, RANDOM_LEN));
MEMCPY(out + (i * RANDOM_LEN), tmp_rnd, RANDOM_LEN);
}

//value commitment randomness + random seed
for (uint8_t i = 0; i < 2 * output_len; i++) {
if (i % 2 == 0) {
CHECK_ZXERR(random_fr(tmp_rnd, RANDOM_LEN));
} else {
cx_rng(tmp_rnd, RANDOM_LEN);
}
MEMCPY(out + (2 * spend_len * RANDOM_LEN) + (i * RANDOM_LEN), tmp_rnd, RANDOM_LEN);
}

//value commitment randomness
for (uint8_t i = 0; i < convert_len; i++) {
CHECK_ZXERR(random_fr(tmp_rnd, RANDOM_LEN));
MEMCPY(out +(2 * spend_len * RANDOM_LEN) + (2 * output_len * RANDOM_LEN) + (i * RANDOM_LEN), tmp_rnd, RANDOM_LEN);
}

*replyLen = (2 * spend_len * RANDOM_LEN) * (2 + output_len * RANDOM_LEN) + (convert_len * RANDOM_LEN);
return zxerr_ok;
}
3 changes: 2 additions & 1 deletion app/src/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ zxerr_t crypto_fillAddress(signing_key_type_e addressKind, uint8_t *buffer, uint
zxerr_t crypto_sign(const parser_tx_t *txObj, uint8_t *output, uint16_t outputLen);
zxerr_t crypto_fillMASP(uint8_t *buffer, uint16_t bufferLen, uint16_t *cmdResponseLen, key_kind_e requestedKey);
zxerr_t crypto_sign_masp(const parser_tx_t *txObj, uint8_t *output, uint16_t outputLen);
zxerr_t crypto_extract_spend_signature(uint8_t *buffer, uint16_t bufferLen) ;
zxerr_t crypto_extract_spend_signature(uint8_t *buffer, uint16_t bufferLen);
zxerr_t crypto_computeRandomness(const uint8_t *buffer, uint16_t bufferLen, uint8_t *out, uint16_t outLen, uint16_t *replyLen);
#ifdef __cplusplus
}
#endif
1 change: 1 addition & 0 deletions app/src/parser_txdef.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ extern "C" {
#define COMPACT_NOTE_SIZE 52
#define NOTE_PLAINTEXT_SIZE 564
#define POSITION_LEN 8
#define RANDOM_LEN 32

#define CMU_OFFSET CV_LEN
#define EPK_OFFSET CMU_OFFSET + CMU_LEN
Expand Down
7 changes: 4 additions & 3 deletions js/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ export const INS = {
SIGN: 0x02,

GET_KEYS: 0x03,
SIGN_MASP: 0x04,
GET_SPEND_SIG: 0x05,
GET_RANDOMNESS: 0x04,
SIGN_MASP: 0x05,
}
export const SALT_LEN = 8
export const HASH_LEN = 32
export const PK_LEN_PLUS_TAG = 33
export const SIG_LEN_PLUS_TAG = 65
export const KEY_LENGTH = 32;
export const KEY_LENGTH = 32
export const RANDOMNESS_LENGTH = 32
130 changes: 108 additions & 22 deletions js/src/namadaApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,28 @@
* limitations under the License.
******************************************************************************* */
import Transport from '@ledgerhq/hw-transport'
import { KeyResponse, NamadaKeys, ResponseAddress, ResponseAppInfo, ResponseBase, ResponseSign, ResponseSignMasp, ResponseVersion } from './types'

import {
CHUNK_SIZE,
errorCodeToString,
LedgerError,
P1_VALUES,
PAYLOAD_TYPE,
processErrorResponse,
serializePath,
} from './common'
KeyResponse,
NamadaKeys,
ResponseAddress,
ResponseAppInfo,
ResponseBase,
ResponseGetRandomness,
ResponseSign,
ResponseSignMasp,
ResponseVersion,
} from './types'

import { CHUNK_SIZE, errorCodeToString, LedgerError, P1_VALUES, PAYLOAD_TYPE, processErrorResponse, serializePath } from './common'

import { CLA, INS } from './config'
import { getSignatureResponse, processGetAddrResponse, processGetKeysResponse, processSpendSignResponse } from './processResponses'
import {
getSignatureResponse,
processGetAddrResponse,
processGetKeysResponse,
processRandomnessResponse,
processSpendSignResponse,
} from './processResponses'

export { LedgerError }
export * from './types'
Expand Down Expand Up @@ -182,13 +190,62 @@ export class NamadaApp {
signature: getSignatureResponse(response),
returnCode,
errorMessage,
};
}
}

return {
returnCode: returnCode,
errorMessage: errorMessage,
} as ResponseSign
} as ResponseGetRandomness
}, processErrorResponse)
}

async sendChunk(
chunkIdx: number,
chunkNum: number,
chunk: Buffer,
ins: number,
n_spends: number,
n_converts: number,
n_outputs: number,
): Promise<ResponseGetRandomness> {
let payloadType = PAYLOAD_TYPE.ADD
const p2 = 0
if (chunkIdx === 1) {
payloadType = PAYLOAD_TYPE.INIT
}
if (chunkIdx === chunkNum) {
payloadType = PAYLOAD_TYPE.LAST
}

return this.transport
.send(CLA, ins, payloadType, p2, chunk, [
LedgerError.NoErrors,
LedgerError.DataIsInvalid,
LedgerError.BadKeyHandle,
LedgerError.SignVerifyError,
])
.then((response: Buffer) => {
const errorCodeData = response.subarray(-2)
const returnCode = errorCodeData[0] * 256 + errorCodeData[1]
let errorMessage = errorCodeToString(returnCode)

if (
returnCode === LedgerError.BadKeyHandle ||
returnCode === LedgerError.DataIsInvalid ||
returnCode === LedgerError.SignVerifyError
) {
errorMessage = `${errorMessage} : ${response.subarray(0, response.length - 2).toString('ascii')}`
}

if (returnCode === LedgerError.NoErrors && response.length > 2) {
return processRandomnessResponse(response, n_spends, n_outputs, n_converts);
}

return {
returnCode: returnCode,
errorMessage: errorMessage,
} as ResponseGetRandomness
}, processErrorResponse)
}

Expand All @@ -214,14 +271,14 @@ export class NamadaApp {
}

async retrieveKeys(path: string, keyType: NamadaKeys, showInDevice: boolean): Promise<KeyResponse> {
const serializedPath = serializePath(path);
const serializedPath = serializePath(path)
const p1 = showInDevice ? P1_VALUES.SHOW_ADDRESS_IN_DEVICE : P1_VALUES.ONLY_RETRIEVE
return this.transport
.send(CLA, INS.GET_KEYS, p1, keyType, serializedPath, [LedgerError.NoErrors])
.then(result => processGetKeysResponse(result, keyType) as KeyResponse, processErrorResponse)
.send(CLA, INS.GET_KEYS, p1, keyType, serializedPath, [LedgerError.NoErrors])
.then(result => processGetKeysResponse(result, keyType) as KeyResponse, processErrorResponse)
}

async signMasp(path:string, masp: Buffer): Promise<ResponseSignMasp> {
async signMasp(path: string, masp: Buffer): Promise<ResponseSignMasp> {
const serializedPath = serializePath(path)
return this.prepareChunks(serializedPath, masp).then(chunks => {
return this.signSendChunk(1, chunks.length, chunks[0], INS.SIGN_MASP).then(async response => {
Expand All @@ -241,9 +298,38 @@ export class NamadaApp {
}, processErrorResponse)
}

async getSignature(): Promise<ResponseSign> {
return this.transport
.send(CLA, INS.GET_SPEND_SIG, P1_VALUES.ONLY_RETRIEVE, 0, Buffer.from([]), [0x9000])
.then(processSpendSignResponse, processErrorResponse);
}
// async getSignature(): Promise<ResponseSign> {
// return this.transport
// .send(CLA, INS.GET_SPEND_SIG, P1_VALUES.ONLY_RETRIEVE, 0, Buffer.from([]), [0x9000])
// .then(processSpendSignResponse, processErrorResponse);
// }

async getMASPRandomness(path: string, n_spends: number, n_outputs: number, n_converts: number): Promise<ResponseGetRandomness> {
if (n_spends < 1 && n_outputs < 1 && n_converts < 1) {
throw new Error('Invalid number of spends, outputs or converts')
}

const serializedPath = serializePath(path)
const data = Buffer.alloc(3)
data.writeUInt8(n_spends, 0)
data.writeUInt8(n_outputs, 1)
data.writeUInt8(n_converts, 2)

return this.prepareChunks(serializedPath, data).then(chunks => {
return this.sendChunk(1, chunks.length, chunks[0], INS.GET_RANDOMNESS, n_spends, n_converts, n_outputs).then(async response => {
let result = {
returnCode: response.returnCode,
errorMessage: response.errorMessage,
}

for (let i = 1; i < chunks.length; i++) {
result = await this.sendChunk(1 + i, chunks.length, chunks[i], INS.GET_RANDOMNESS, n_spends, n_converts, n_outputs)
if (result.returnCode !== LedgerError.NoErrors) {
break
}
}
return result
}, processErrorResponse)
}, processErrorResponse)
}
}
Loading

0 comments on commit c6de873

Please sign in to comment.