Skip to content
Permalink
7d6c5f5573
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
415 lines (352 sloc) 12.1 KB
/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, Ledger SAS
*
* 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 "os.h"
#include "cx.h"
#include "monero_types.h"
#include "monero_api.h"
#include "monero_vars.h"
#if defined(IODUMMYCRYPT)
#warning IODUMMYCRYPT activated
#endif
#if defined(IONOCRYPT)
#warning IONOCRYPT activated
#endif
/*
* io_buff: contains current message part
* io_off: offset in current message part
* io_length: length of current message part
*/
/* ----------------------------------------------------------------------- */
/* MISC */
/* ----------------------------------------------------------------------- */
void monero_io_set_offset(unsigned int offset) {
if (offset == IO_OFFSET_END) {
G_monero_vstate.io_offset = G_monero_vstate.io_length;
}
else if (offset == IO_OFFSET_MARK) {
G_monero_vstate.io_offset = G_monero_vstate.io_mark;
}
else if (offset < G_monero_vstate.io_length) {
G_monero_vstate.io_offset = offset;
}
else {
THROW(ERROR_IO_OFFSET);
return ;
}
}
void monero_io_mark() {
G_monero_vstate.io_mark = G_monero_vstate.io_offset;
}
void monero_io_inserted(unsigned int len) {
G_monero_vstate.io_offset += len;
G_monero_vstate.io_length += len;
}
void monero_io_discard(int clear) {
G_monero_vstate.io_length = 0;
G_monero_vstate.io_offset = 0;
G_monero_vstate.io_mark = 0;
if (clear) {
monero_io_clear();
}
}
void monero_io_clear() {
os_memset(G_monero_vstate.io_buffer, 0 , MONERO_IO_BUFFER_LENGTH);
}
/* ----------------------------------------------------------------------- */
/* INSERT data to be sent */
/* ----------------------------------------------------------------------- */
void monero_io_hole(unsigned int sz) {
if ((G_monero_vstate.io_length + sz) > MONERO_IO_BUFFER_LENGTH) {
THROW(ERROR_IO_FULL);
return ;
}
os_memmove(G_monero_vstate.io_buffer+G_monero_vstate.io_offset+sz,
G_monero_vstate.io_buffer+G_monero_vstate.io_offset,
G_monero_vstate.io_length-G_monero_vstate.io_offset);
G_monero_vstate.io_length += sz;
}
void monero_io_insert(unsigned char const *buff, unsigned int len) {
monero_io_hole(len);
os_memmove(G_monero_vstate.io_buffer+G_monero_vstate.io_offset, buff, len);
G_monero_vstate.io_offset += len;
}
void monero_io_insert_hmac_for(unsigned char* buffer, int len) {
unsigned char hmac[32];
cx_hmac_sha256(G_monero_vstate.hmac_key, 32, buffer, len, hmac, 32);
monero_io_insert(hmac,32);
}
void monero_io_insert_encrypt(unsigned char* buffer, int len) {
//for now, only 32bytes block are allowed
if (len != 32) {
THROW(SW_WRONG_DATA);
return ;
}
monero_io_hole(len);
#if defined(IODUMMYCRYPT)
for (int i = 0; i<len; i++) {
G_monero_vstate.io_buffer[G_monero_vstate.io_offset+i] = buffer[i] ^ 0x55;
}
#elif defined(IONOCRYPT)
os_memmove(G_monero_vstate.io_buffer+G_monero_vstate.io_offset, buffer, len);
#else
cx_aes(&G_monero_vstate.spk, CX_ENCRYPT|CX_CHAIN_CBC|CX_LAST|CX_PAD_NONE,
buffer, len,
G_monero_vstate.io_buffer+G_monero_vstate.io_offset, len);
#endif
G_monero_vstate.io_offset += len;
if (G_monero_vstate.tx_in_progress) {
monero_io_insert_hmac_for(G_monero_vstate.io_buffer+G_monero_vstate.io_offset-len, len);
}
}
void monero_io_insert_u32(unsigned int v32) {
monero_io_hole(4);
G_monero_vstate.io_buffer[G_monero_vstate.io_offset+0] = v32>>24;
G_monero_vstate.io_buffer[G_monero_vstate.io_offset+1] = v32>>16;
G_monero_vstate.io_buffer[G_monero_vstate.io_offset+2] = v32>>8;
G_monero_vstate.io_buffer[G_monero_vstate.io_offset+3] = v32>>0;
G_monero_vstate.io_offset += 4;
}
void monero_io_insert_u24(unsigned int v24) {
monero_io_hole(3);
G_monero_vstate.io_buffer[G_monero_vstate.io_offset+0] = v24>>16;
G_monero_vstate.io_buffer[G_monero_vstate.io_offset+1] = v24>>8;
G_monero_vstate.io_buffer[G_monero_vstate.io_offset+2] = v24>>0;
G_monero_vstate.io_offset += 3;
}
void monero_io_insert_u16(unsigned int v16) {
monero_io_hole(2);
G_monero_vstate.io_buffer[G_monero_vstate.io_offset+0] = v16>>8;
G_monero_vstate.io_buffer[G_monero_vstate.io_offset+1] = v16>>0;
G_monero_vstate.io_offset += 2;
}
void monero_io_insert_u8(unsigned int v8) {
monero_io_hole(1);
G_monero_vstate.io_buffer[G_monero_vstate.io_offset+0] = v8;
G_monero_vstate.io_offset += 1;
}
void monero_io_insert_t(unsigned int T) {
if (T &0xFF00) {
monero_io_insert_u16(T);
} else {
monero_io_insert_u8(T);
}
}
void monero_io_insert_tl(unsigned int T, unsigned int L) {
monero_io_insert_t(T);
if (L < 128) {
monero_io_insert_u8(L);
} else if (L < 256) {
monero_io_insert_u16(0x8100|L);
} else {
monero_io_insert_u8(0x82);
monero_io_insert_u16(L);
}
}
void monero_io_insert_tlv(unsigned int T, unsigned int L, unsigned char const *V) {
monero_io_insert_tl(T,L);
monero_io_insert(V, L);
}
/* ----------------------------------------------------------------------- */
/* FECTH data from received buffer */
/* ----------------------------------------------------------------------- */
void monero_io_assert_available(int sz) {
if ((G_monero_vstate.io_length-G_monero_vstate.io_offset) < sz) {
THROW(SW_WRONG_LENGTH + (sz&0xFF));
}
}
int monero_io_fetch(unsigned char* buffer, int len) {
monero_io_assert_available(len);
if (buffer) {
os_memmove(buffer, G_monero_vstate.io_buffer+G_monero_vstate.io_offset, len);
}
G_monero_vstate.io_offset += len;
return len;
}
static void monero_io_verify_hmac_for(const unsigned char* buffer, int len, unsigned char *expected_hmac) {
unsigned char hmac[32];
cx_hmac_sha256(G_monero_vstate.hmac_key, 32,
buffer, len,
hmac, 32);
if (os_memcmp(hmac, expected_hmac, 32)) {
THROW(SW_SECURITY_TRUSTED_INPUT);
}
}
int monero_io_fetch_decrypt(unsigned char* buffer, int len) {
//for now, only 32bytes block allowed
if (len != 32) {
THROW(SW_SECURE_MESSAGING_NOT_SUPPORTED);
return 0;
}
if (G_monero_vstate.tx_in_progress) {
monero_io_assert_available(len+32);
monero_io_verify_hmac_for(G_monero_vstate.io_buffer+G_monero_vstate.io_offset, len,
G_monero_vstate.io_buffer+G_monero_vstate.io_offset + len);
} else {
monero_io_assert_available(len);
}
if (buffer) {
#if defined(IODUMMYCRYPT)
for (int i = 0; i<len; i++) {
buffer[i] = G_monero_vstate.io_buffer[G_monero_vstate.io_offset+i] ^ 0x55;
}
#elif defined(IONOCRYPT)
os_memmove(buffer, G_monero_vstate.io_buffer+G_monero_vstate.io_offset, len);
#else //IOCRYPT
cx_aes(&G_monero_vstate.spk, CX_DECRYPT|CX_CHAIN_CBC|CX_LAST|CX_PAD_NONE,
G_monero_vstate.io_buffer+G_monero_vstate.io_offset, len,
buffer, len);
#endif
}
G_monero_vstate.io_offset += len;
if (G_monero_vstate.tx_in_progress) {
G_monero_vstate.io_offset += 32;
}
return len;
}
int monero_io_fetch_decrypt_key(unsigned char* buffer) {
unsigned char* k;
monero_io_assert_available(32);
k = G_monero_vstate.io_buffer+G_monero_vstate.io_offset;
//view?
if (os_memcmp(k, C_FAKE_SEC_VIEW_KEY, 32)==0) {
G_monero_vstate.io_offset += 32;
if (G_monero_vstate.tx_in_progress) {
monero_io_assert_available(32);
monero_io_verify_hmac_for(C_FAKE_SEC_VIEW_KEY, 32, G_monero_vstate.io_buffer+G_monero_vstate.io_offset);
G_monero_vstate.io_offset += 32;
}
os_memmove(buffer, G_monero_vstate.a,32);
return 32;
}
//spend?
else if (os_memcmp(k, C_FAKE_SEC_SPEND_KEY, 32)==0) {
G_monero_vstate.io_offset += 32;
if (G_monero_vstate.tx_in_progress) {
monero_io_assert_available(32);
monero_io_verify_hmac_for(C_FAKE_SEC_SPEND_KEY, 32, G_monero_vstate.io_buffer+G_monero_vstate.io_offset);
}
os_memmove(buffer, G_monero_vstate.b,32);
return 32;
}
//else
else {
return monero_io_fetch_decrypt(buffer, 32);
}
}
unsigned int monero_io_fetch_u32() {
unsigned int v32;
monero_io_assert_available(4);
v32 = ( (G_monero_vstate.io_buffer[G_monero_vstate.io_offset+0] << 24) |
(G_monero_vstate.io_buffer[G_monero_vstate.io_offset+1] << 16) |
(G_monero_vstate.io_buffer[G_monero_vstate.io_offset+2] << 8) |
(G_monero_vstate.io_buffer[G_monero_vstate.io_offset+3] << 0) );
G_monero_vstate.io_offset += 4;
return v32;
}
unsigned int monero_io_fetch_u24() {
unsigned int v24;
monero_io_assert_available(3);
v24 = ( (G_monero_vstate.io_buffer[G_monero_vstate.io_offset+0] << 16) |
(G_monero_vstate.io_buffer[G_monero_vstate.io_offset+1] << 8) |
(G_monero_vstate.io_buffer[G_monero_vstate.io_offset+2] << 0) );
G_monero_vstate.io_offset += 3;
return v24;
}
unsigned int monero_io_fetch_u16() {
unsigned int v16;
monero_io_assert_available(2);
v16 = ( (G_monero_vstate.io_buffer[G_monero_vstate.io_offset+0] << 8) |
(G_monero_vstate.io_buffer[G_monero_vstate.io_offset+1] << 0) );
G_monero_vstate.io_offset += 2;
return v16;
}
unsigned int monero_io_fetch_u8() {
unsigned int v8;
monero_io_assert_available(1);
v8 = G_monero_vstate.io_buffer[G_monero_vstate.io_offset] ;
G_monero_vstate.io_offset += 1;
return v8;
}
int monero_io_fetch_t(unsigned int *T) {
*T = monero_io_fetch_u8();
if ((*T & 0x1F) == 0x1F) {
*T = (*T << 8) | monero_io_fetch_u8();
}
return 0;
}
int monero_io_fetch_l(unsigned int *L) {
*L = monero_io_fetch_u8();
if ((*L & 0x80) != 0) {
*L &= 0x7F;
if (*L == 1) {
*L = monero_io_fetch_u8();
} else if (*L == 2) {
*L = monero_io_fetch_u16() ;
} else {
*L = -1;
}
}
return 0;
}
int monero_io_fetch_tl(unsigned int *T, unsigned int *L) {
monero_io_fetch_t(T);
monero_io_fetch_l(L);
return 0;
}
int monero_io_fetch_nv(unsigned char* buffer, int len) {
monero_io_assert_available(len);
monero_nvm_write(buffer, G_monero_vstate.io_buffer+G_monero_vstate.io_offset, len);
G_monero_vstate.io_offset += len;
return len;
}
/* ----------------------------------------------------------------------- */
/* REAL IO */
/* ----------------------------------------------------------------------- */
#define MAX_OUT MONERO_APDU_LENGTH
int monero_io_do(unsigned int io_flags) {
// if IO_ASYNCH_REPLY has been set,
// monero_io_exchange will return when IO_RETURN_AFTER_TX will set in ui
if (io_flags & IO_ASYNCH_REPLY) {
monero_io_exchange(CHANNEL_APDU | IO_ASYNCH_REPLY, 0);
}
//else send data now
else {
G_monero_vstate.io_offset = 0;
if(G_monero_vstate.io_length > MAX_OUT) {
THROW(SW_FILE_FULL);
return SW_FILE_FULL;
}
os_memmove(G_io_apdu_buffer, G_monero_vstate.io_buffer+G_monero_vstate.io_offset, G_monero_vstate.io_length);
if (io_flags & IO_RETURN_AFTER_TX) {
monero_io_exchange(CHANNEL_APDU |IO_RETURN_AFTER_TX, G_monero_vstate.io_length);
return 0;
} else {
monero_io_exchange(CHANNEL_APDU, G_monero_vstate.io_length);
}
}
//--- set up received data ---
G_monero_vstate.io_offset = 0;
G_monero_vstate.io_length = 0;
G_monero_vstate.io_protocol_version = G_io_apdu_buffer[0];
G_monero_vstate.io_ins = G_io_apdu_buffer[1];
G_monero_vstate.io_p1 = G_io_apdu_buffer[2];
G_monero_vstate.io_p2 = G_io_apdu_buffer[3];
G_monero_vstate.io_lc = 0;
G_monero_vstate.io_le = 0;
G_monero_vstate.io_lc = G_io_apdu_buffer[4];
os_memmove(G_monero_vstate.io_buffer, G_io_apdu_buffer+5, G_monero_vstate.io_lc);
G_monero_vstate.io_length = G_monero_vstate.io_lc;
return 0;
}