340 lines
12 KiB
C
340 lines
12 KiB
C
/*******************************************************************************
|
|
* Ledger Blue
|
|
* (c) 2016 Ledger
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
********************************************************************************/
|
|
|
|
#include "ethUstream.h"
|
|
#include "ethUtils.h"
|
|
|
|
#define MAX_INT256 32
|
|
#define MAX_ADDRESS 20
|
|
|
|
void initTx(txContext_t *context, app_cx_sha3_t *sha3, txContent_t *content,
|
|
ustreamProcess_t customProcessor, void *extra) {
|
|
os_memset(context, 0, sizeof(txContext_t));
|
|
context->sha3 = sha3;
|
|
context->content = content;
|
|
context->customProcessor = customProcessor;
|
|
context->extra = extra;
|
|
context->currentField = TX_RLP_CONTENT;
|
|
app_cx_sha3_init(context->sha3, 256);
|
|
}
|
|
|
|
uint8_t readTxByte(txContext_t *context) {
|
|
uint8_t data;
|
|
if (context->commandLength < 1) {
|
|
screen_printf("readTxByte Underflow\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
data = *context->workBuffer;
|
|
context->workBuffer++;
|
|
context->commandLength--;
|
|
if (context->processingField) {
|
|
context->currentFieldPos++;
|
|
}
|
|
if (!(context->processingField && context->fieldSingleByte)) {
|
|
app_cx_hash((cx_hash_t*)context->sha3, 0, &data, 1, NULL);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
void copyTxData(txContext_t *context, uint8_t *out, uint32_t length) {
|
|
if (context->commandLength < length) {
|
|
screen_printf("copyTxData Underflow\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
if (out != NULL) {
|
|
os_memmove(out, context->workBuffer, length);
|
|
}
|
|
if (!(context->processingField && context->fieldSingleByte)) {
|
|
app_cx_hash((cx_hash_t*)context->sha3, 0, context->workBuffer, length, NULL);
|
|
}
|
|
context->workBuffer += length;
|
|
context->commandLength -= length;
|
|
if (context->processingField) {
|
|
context->currentFieldPos += length;
|
|
}
|
|
}
|
|
|
|
static void processContent(txContext_t *context) {
|
|
// Keep the full length for sanity checks, move to the next field
|
|
if (!context->currentFieldIsList) {
|
|
screen_printf("Invalid type for RLP_CONTENT\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
context->dataLength = context->currentFieldLength;
|
|
context->currentField++;
|
|
context->processingField = false;
|
|
}
|
|
|
|
static void processNonce(txContext_t *context) {
|
|
if (context->currentFieldIsList) {
|
|
screen_printf("Invalid type for RLP_NONCE\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
if (context->currentFieldLength > MAX_INT256) {
|
|
screen_printf("Invalid length for RLP_NONCE\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
if (context->currentFieldPos < context->currentFieldLength) {
|
|
uint32_t copySize =
|
|
(context->commandLength <
|
|
((context->currentFieldLength - context->currentFieldPos))
|
|
? context->commandLength
|
|
: context->currentFieldLength - context->currentFieldPos);
|
|
copyTxData(context, NULL, copySize);
|
|
}
|
|
if (context->currentFieldPos == context->currentFieldLength) {
|
|
context->currentField++;
|
|
context->processingField = false;
|
|
}
|
|
}
|
|
|
|
static void processStartGas(txContext_t *context) {
|
|
if (context->currentFieldIsList) {
|
|
screen_printf("Invalid type for RLP_STARTGAS\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
if (context->currentFieldLength > MAX_INT256) {
|
|
screen_printf("Invalid length for RLP_STARTGAS %d\n",
|
|
context->currentFieldLength);
|
|
THROW(EXCEPTION);
|
|
}
|
|
if (context->currentFieldPos < context->currentFieldLength) {
|
|
uint32_t copySize =
|
|
(context->commandLength <
|
|
((context->currentFieldLength - context->currentFieldPos))
|
|
? context->commandLength
|
|
: context->currentFieldLength - context->currentFieldPos);
|
|
copyTxData(context,
|
|
context->content->startgas.value + context->currentFieldPos,
|
|
copySize);
|
|
}
|
|
if (context->currentFieldPos == context->currentFieldLength) {
|
|
context->content->startgas.length = context->currentFieldLength;
|
|
context->currentField++;
|
|
context->processingField = false;
|
|
}
|
|
}
|
|
|
|
static void processGasprice(txContext_t *context) {
|
|
if (context->currentFieldIsList) {
|
|
screen_printf("Invalid type for RLP_GASPRICE\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
if (context->currentFieldLength > MAX_INT256) {
|
|
screen_printf("Invalid length for RLP_GASPRICE\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
if (context->currentFieldPos < context->currentFieldLength) {
|
|
uint32_t copySize =
|
|
(context->commandLength <
|
|
((context->currentFieldLength - context->currentFieldPos))
|
|
? context->commandLength
|
|
: context->currentFieldLength - context->currentFieldPos);
|
|
copyTxData(context,
|
|
context->content->gasprice.value + context->currentFieldPos,
|
|
copySize);
|
|
}
|
|
if (context->currentFieldPos == context->currentFieldLength) {
|
|
context->content->gasprice.length = context->currentFieldLength;
|
|
context->currentField++;
|
|
context->processingField = false;
|
|
}
|
|
}
|
|
|
|
static void processValue(txContext_t *context) {
|
|
if (context->currentFieldIsList) {
|
|
screen_printf("Invalid type for RLP_VALUE\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
if (context->currentFieldLength > MAX_INT256) {
|
|
screen_printf("Invalid length for RLP_VALUE\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
if (context->currentFieldPos < context->currentFieldLength) {
|
|
uint32_t copySize =
|
|
(context->commandLength <
|
|
((context->currentFieldLength - context->currentFieldPos))
|
|
? context->commandLength
|
|
: context->currentFieldLength - context->currentFieldPos);
|
|
copyTxData(context,
|
|
context->content->value.value + context->currentFieldPos,
|
|
copySize);
|
|
}
|
|
if (context->currentFieldPos == context->currentFieldLength) {
|
|
context->content->value.length = context->currentFieldLength;
|
|
context->currentField++;
|
|
context->processingField = false;
|
|
}
|
|
}
|
|
|
|
static void processTo(txContext_t *context) {
|
|
if (context->currentFieldIsList) {
|
|
screen_printf("Invalid type for RLP_TO\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
if (context->currentFieldLength > MAX_ADDRESS) {
|
|
screen_printf("Invalid length for RLP_TO\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
if (context->currentFieldPos < context->currentFieldLength) {
|
|
uint32_t copySize =
|
|
(context->commandLength <
|
|
((context->currentFieldLength - context->currentFieldPos))
|
|
? context->commandLength
|
|
: context->currentFieldLength - context->currentFieldPos);
|
|
copyTxData(context,
|
|
context->content->destination + context->currentFieldPos,
|
|
copySize);
|
|
}
|
|
if (context->currentFieldPos == context->currentFieldLength) {
|
|
context->currentField++;
|
|
context->processingField = false;
|
|
}
|
|
}
|
|
|
|
static void processData(txContext_t *context) {
|
|
if (context->currentFieldIsList) {
|
|
screen_printf("Invalid type for RLP_DATA\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
if (context->currentFieldLength > MAX_INT256) {
|
|
screen_printf("Invalid length for RLP_DATA\n");
|
|
THROW(EXCEPTION);
|
|
}
|
|
if (context->currentFieldPos < context->currentFieldLength) {
|
|
uint32_t copySize =
|
|
(context->commandLength <
|
|
((context->currentFieldLength - context->currentFieldPos))
|
|
? context->commandLength
|
|
: context->currentFieldLength - context->currentFieldPos);
|
|
copyTxData(context, NULL, copySize);
|
|
}
|
|
if (context->currentFieldPos == context->currentFieldLength) {
|
|
context->currentField++;
|
|
context->processingField = false;
|
|
}
|
|
}
|
|
|
|
static parserStatus_e processTxInternal(txContext_t *context) {
|
|
for (;;) {
|
|
bool processedCustom = false;
|
|
if (context->currentField == TX_RLP_DONE) {
|
|
return USTREAM_FINISHED;
|
|
}
|
|
if (context->commandLength == 0) {
|
|
return USTREAM_PROCESSING;
|
|
}
|
|
if (!context->processingField) {
|
|
bool canDecode = false;
|
|
uint32_t offset;
|
|
while (context->commandLength != 0) {
|
|
bool valid;
|
|
// Feed the RLP buffer until the length can be decoded
|
|
context->rlpBuffer[context->rlpBufferPos++] =
|
|
readTxByte(context);
|
|
if (rlpCanDecode(context->rlpBuffer, context->rlpBufferPos,
|
|
&valid)) {
|
|
// Can decode now, if valid
|
|
if (!valid) {
|
|
screen_printf("RLP pre-decode error\n");
|
|
return USTREAM_FAULT;
|
|
}
|
|
canDecode = true;
|
|
break;
|
|
}
|
|
// Cannot decode yet
|
|
// Sanity check
|
|
if (context->rlpBufferPos == sizeof(context->rlpBuffer)) {
|
|
screen_printf("RLP pre-decode logic error\n");
|
|
return USTREAM_FAULT;
|
|
}
|
|
}
|
|
if (!canDecode) {
|
|
return USTREAM_PROCESSING;
|
|
}
|
|
// Ready to process this field
|
|
if (!rlpDecodeLength(context->rlpBuffer, context->rlpBufferPos,
|
|
&context->currentFieldLength, &offset,
|
|
&context->currentFieldIsList)) {
|
|
screen_printf("RLP decode error\n");
|
|
return USTREAM_FAULT;
|
|
}
|
|
if (offset == 0) {
|
|
// Hack for single byte, self encoded
|
|
context->workBuffer--;
|
|
context->commandLength++;
|
|
context->fieldSingleByte = true;
|
|
} else {
|
|
context->fieldSingleByte = false;
|
|
}
|
|
context->currentFieldPos = 0;
|
|
context->rlpBufferPos = 0;
|
|
context->processingField = true;
|
|
}
|
|
if (context->customProcessor != NULL) {
|
|
processedCustom = context->customProcessor(context);
|
|
}
|
|
if (!processedCustom) {
|
|
switch (context->currentField) {
|
|
case TX_RLP_CONTENT:
|
|
processContent(context);
|
|
break;
|
|
case TX_RLP_NONCE:
|
|
processNonce(context);
|
|
break;
|
|
case TX_RLP_GASPRICE:
|
|
processGasprice(context);
|
|
break;
|
|
case TX_RLP_STARTGAS:
|
|
processStartGas(context);
|
|
break;
|
|
case TX_RLP_VALUE:
|
|
processValue(context);
|
|
break;
|
|
case TX_RLP_TO:
|
|
processTo(context);
|
|
break;
|
|
case TX_RLP_DATA:
|
|
processData(context);
|
|
break;
|
|
default:
|
|
screen_printf("Invalid RLP decoder context\n");
|
|
return USTREAM_FAULT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
parserStatus_e processTx(txContext_t *context, uint8_t *buffer,
|
|
uint32_t length) {
|
|
parserStatus_e result;
|
|
BEGIN_TRY {
|
|
TRY {
|
|
context->workBuffer = buffer;
|
|
context->commandLength = length;
|
|
result = processTxInternal(context);
|
|
}
|
|
CATCH_OTHER(e) {
|
|
result = USTREAM_FAULT;
|
|
}
|
|
FINALLY {
|
|
}
|
|
}
|
|
END_TRY;
|
|
return result;
|
|
}
|