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/ Support ethereum derivation path and remove warnings #1

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4a24915
test: write test for get address with ethereum derivation path
keiff3r Mar 12, 2024
e657cdf
feat: Add Ethereum paths to Makefile
keiff3r Mar 12, 2024
43f850c
chore: generate golden path snapshot for the new test
keiff3r Mar 12, 2024
3ad5c5b
test: write tests to validate using ethereum derivation path
keiff3r Mar 12, 2024
2afc50f
chore: generate screens to validate tests (for ethereum derivation path)
keiff3r Mar 12, 2024
7755d03
fix: Update ETH_PACKED_DERIVATION_PATH in celo_utils.py
keiff3r Mar 12, 2024
2ea6740
chore: Update test snapshots for Ethereum path derivation
keiff3r Mar 12, 2024
8fb5be2
refactor: change eth support implementation
keiff3r Mar 14, 2024
f4cd02f
fix: add eth derivation path to match ci
keiff3r Mar 14, 2024
3fc8b41
refactor: change implementation of eth support
keiff3r Mar 14, 2024
347004e
feat: enable optimisation, remove support for eth path
keiff3r Mar 14, 2024
682c7ef
feat: add Ethereum path in Makefile
keiff3r Mar 14, 2024
9d4a256
chore: generate new test snapshots for nanosp, nano X and stax
keiff3r Mar 14, 2024
857a4e7
fix(celo.c): add multiple breaks to remove a warning
keiff3r Mar 14, 2024
8d2786e
fix: Update cx_keccak_init calls to cx_keccak_init_no_throw
keiff3r Mar 14, 2024
2f2f601
fix: Update ticker and feeTicker to const char pointers
keiff3r Mar 14, 2024
d69cb3b
fix: change cx_hash to cx_hash_no_throw
keiff3r Mar 14, 2024
cd2a5c8
fix: change os_perso_derive_node_bip32 calls to os_derive_bip32_no_throw
keiff3r Mar 14, 2024
2c3e39a
fix: remove all 'warn_unused_result' warnings
keiff3r Mar 14, 2024
c6e7b83
fix: change all deprecated functions calls with recommanded functions
keiff3r Mar 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)

# Celo
APP_LOAD_PARAMS += --path "44'/52752'"
# Ethereum path
APP_LOAD_PARAMS += --path "44'/60'/0'/0/0" --path "44'/60'/0'" --path "44'/60'/0'/0"

APPNAME = "Celo"
APP_LOAD_FLAGS=--appFlags 0
ifeq ($(TARGET_NAME),$(filter $(TARGET_NAME),TARGET_NANOX TARGET_STAX))
Expand Down Expand Up @@ -133,7 +136,7 @@ endif

CC := $(CLANGPATH)clang

#CFLAGS += -O0
# CFLAGS += -O0
CFLAGS += -O3 -Os

AS := $(GCCPATH)arm-none-eabi-gcc
Expand Down
14 changes: 10 additions & 4 deletions src/celo.c
Original file line number Diff line number Diff line change
Expand Up @@ -232,15 +232,16 @@ customStatus_e customProcessor(txContext_t *context) {
copyTxData(context,
dataContext.withdrawContext.data + context->currentFieldPos,
copySize);
break;
case PROVISION_RELOCK:
copyTxData(context,
dataContext.relockContext.data + context->currentFieldPos,
copySize);
break;
case PROVISION_CREATE_ACCOUNT:
copyTxData(context,
dataContext.createAccountContext.data + context->currentFieldPos,
copySize);

break;
default:
break;
Expand Down Expand Up @@ -331,8 +332,8 @@ void finalizeParsing(bool direct) {
uint32_t i;
uint8_t decimals = WEI_TO_ETHER;
uint8_t feeDecimals = WEI_TO_ETHER;
char *ticker = CHAINID_COINNAME " ";
char *feeTicker = CHAINID_COINNAME " ";
const char *ticker = CHAINID_COINNAME " ";
const char *feeTicker = CHAINID_COINNAME " ";
uint8_t tickerOffset = 0;

// Display correct currency if fee currency field sent
Expand All @@ -356,7 +357,12 @@ void finalizeParsing(bool direct) {
}

// Store the hash
cx_hash((cx_hash_t *)&sha3, CX_LAST, tmpCtx.transactionContext.hash, 0, tmpCtx.transactionContext.hash, 32);
CX_THROW(cx_hash_no_throw((cx_hash_t *) &sha3,
CX_LAST,
tmpCtx.transactionContext.hash,
0,
tmpCtx.transactionContext.hash,
32));
// If there is a token to process, check if it is well known
if (provisionType == PROVISION_TOKEN) {
tokenDefinition_t *currentToken = getKnownToken(tmpContent.txContent.destination);
Expand Down
48 changes: 32 additions & 16 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,10 @@ void handleGetWalletId(volatile unsigned int *tx) {
cx_ecfp_256_private_key_t priv;
cx_ecfp_256_public_key_t pub;
// seed => priv key
os_perso_derive_node_bip32(CX_CURVE_256K1, U_os_perso_seed_cookie, 2, t, NULL);
CX_THROW(os_derive_bip32_no_throw(CX_CURVE_256K1, U_os_perso_seed_cookie, 2, t, NULL));
// priv key => pubkey
cx_ecdsa_init_private_key(CX_CURVE_256K1, t, 32, &priv);
cx_ecfp_generate_pair(CX_CURVE_256K1, &pub, &priv, 1);
CX_THROW(cx_ecdsa_init_private_key(CX_CURVE_256K1, t, 32, &priv));
CX_THROW(cx_ecfp_generate_pair_no_throw(CX_CURVE_256K1, &pub, &priv, 1));
// pubkey -> sha512
cx_hash_sha512(pub.W, sizeof(pub.W), t, sizeof(t));
// ! cookie !
Expand All @@ -184,7 +184,7 @@ void handleGetWalletId(volatile unsigned int *tx) {

void handleGetPublicKey(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t dataLength, volatile unsigned int *flags, volatile unsigned int *tx) {
UNUSED(dataLength);
uint8_t privateKeyData[32];
uint8_t privateKeyData[64];
bip32Path_t derivationPath;
cx_ecfp_private_key_t privateKey;

Expand All @@ -203,10 +203,16 @@ void handleGetPublicKey(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, uint16_t da

tmpCtx.publicKeyContext.getChaincode = (p2 == P2_CHAINCODE);
io_seproxyhal_io_heartbeat();
os_perso_derive_node_bip32(CX_CURVE_256K1, derivationPath.path, derivationPath.len, privateKeyData, (tmpCtx.publicKeyContext.getChaincode ? tmpCtx.publicKeyContext.chainCode : NULL));
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);
CX_THROW(os_derive_bip32_no_throw(
CX_CURVE_256K1,
derivationPath.path,
derivationPath.len,
privateKeyData,
(tmpCtx.publicKeyContext.getChaincode ? tmpCtx.publicKeyContext.chainCode : NULL)));

CX_THROW(cx_ecfp_init_private_key_no_throw(CX_CURVE_256K1, privateKeyData, 32, &privateKey));
io_seproxyhal_io_heartbeat();
cx_ecfp_generate_pair(CX_CURVE_256K1, &tmpCtx.publicKeyContext.publicKey, &privateKey, 1);
CX_THROW(cx_ecfp_generate_pair_no_throw(CX_CURVE_256K1, &tmpCtx.publicKeyContext.publicKey, &privateKey, 1));
explicit_bzero(&privateKey, sizeof(privateKey));
explicit_bzero(privateKeyData, sizeof(privateKeyData));
io_seproxyhal_io_heartbeat();
Expand Down Expand Up @@ -272,8 +278,8 @@ void handleProvideErc20TokenInformation(uint8_t p1, uint8_t p2, uint8_t *workBuf
// Skip chainId
offset += 4;
dataLength -= 4;
cx_ecfp_init_public_key(CX_CURVE_256K1, TOKEN_SIGNATURE_PUBLIC_KEY, sizeof(TOKEN_SIGNATURE_PUBLIC_KEY), &tokenKey);
if (!cx_ecdsa_verify(&tokenKey, CX_LAST, CX_SHA256, hash, 32, workBuffer + offset, dataLength)) {
CX_THROW(cx_ecfp_init_public_key_no_throw(CX_CURVE_256K1, TOKEN_SIGNATURE_PUBLIC_KEY, sizeof(TOKEN_SIGNATURE_PUBLIC_KEY), &tokenKey));
if (!cx_ecdsa_verify_no_throw(&tokenKey, hash, 32, workBuffer + offset, dataLength)) {
PRINTF("Invalid token signature\n");
THROW(0x6A80);
}
Expand Down Expand Up @@ -398,16 +404,24 @@ void handleSignPersonalMessage(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint
workBuffer += 4;
dataLength -= 4;
// Initialize message header + length
cx_keccak_init(&sha3, 256);
cx_hash((cx_hash_t *)&sha3, 0, (uint8_t*)SIGN_MAGIC, sizeof(SIGN_MAGIC) - 1, NULL, 0);
CX_THROW(cx_keccak_init_no_throw(&sha3, 256));
CX_THROW(cx_hash_no_throw((cx_hash_t *)&sha3,
0,
(uint8_t*)SIGN_MAGIC,
sizeof(SIGN_MAGIC) - 1,
NULL,
0));

for (i = 1; (((i * base) <= tmpCtx.messageSigningContext.remainingLength) &&
(((i * base) / base) == i));
i *= base);
for (; i; i /= base) {
tmp[pos++] = '0' + ((tmpCtx.messageSigningContext.remainingLength / i) % base);
}
tmp[pos] = '\0';
cx_hash((cx_hash_t *)&sha3, 0, (uint8_t*)tmp, pos, NULL, 0);

CX_THROW(cx_hash_no_throw((cx_hash_t *) &sha3, 0, (uint8_t*)tmp, pos, NULL, 0));

cx_sha256_init(&tmpContent.sha2);
}
else if (p1 != P1_MORE) {
Expand All @@ -423,14 +437,14 @@ void handleSignPersonalMessage(uint8_t p1, uint8_t p2, uint8_t *workBuffer, uint
if (dataLength > tmpCtx.messageSigningContext.remainingLength) {
THROW(0x6A80);
}
cx_hash((cx_hash_t *)&sha3, 0, workBuffer, dataLength, NULL, 0);
cx_hash((cx_hash_t *)&tmpContent.sha2, 0, workBuffer, dataLength, NULL, 0);
CX_THROW(cx_hash_no_throw((cx_hash_t *)&sha3, 0, workBuffer, dataLength, NULL, 0));
CX_THROW(cx_hash_no_throw((cx_hash_t *)&tmpContent.sha2, 0, workBuffer, dataLength, NULL, 0));
tmpCtx.messageSigningContext.remainingLength -= dataLength;
if (tmpCtx.messageSigningContext.remainingLength == 0) {
uint8_t hashMessage[32];

cx_hash((cx_hash_t *)&sha3, CX_LAST, workBuffer, 0, tmpCtx.messageSigningContext.hash, 32);
cx_hash((cx_hash_t *)&tmpContent.sha2, CX_LAST, workBuffer, 0, hashMessage, 32);
CX_THROW(cx_hash_no_throw((cx_hash_t *)&sha3, CX_LAST, workBuffer, 0, tmpCtx.messageSigningContext.hash, 32));
CX_THROW(cx_hash_no_throw((cx_hash_t *)&tmpContent.sha2, CX_LAST, workBuffer, 0, hashMessage, 32));

#ifdef HAVE_BAGL
#define HASH_LENGTH 4
Expand Down Expand Up @@ -648,6 +662,8 @@ unsigned char io_event(unsigned char channel) {
THROW(EXCEPTION_IO_RESET);
}
// no break is intentional
__attribute__((fallthrough)); // ignore fall-through warning

default:
UX_DEFAULT_EVENT();
break;
Expand Down
31 changes: 17 additions & 14 deletions src/ui_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,24 @@ unsigned int io_seproxyhal_touch_address_cancel(void) {
}

unsigned int io_seproxyhal_touch_tx_ok(void) {
uint8_t privateKeyData[32];
uint8_t privateKeyData[64];
uint8_t signature[100];
cx_ecfp_private_key_t privateKey;
uint32_t tx = 0;
uint32_t v = getV(&tmpContent.txContent);
io_seproxyhal_io_heartbeat();
os_perso_derive_node_bip32(CX_CURVE_256K1, tmpCtx.transactionContext.derivationPath.path,
CX_THROW(os_derive_bip32_no_throw(CX_CURVE_256K1, tmpCtx.transactionContext.derivationPath.path,
tmpCtx.transactionContext.derivationPath.len,
privateKeyData, NULL);
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32,
&privateKey);
privateKeyData, NULL));
CX_THROW(cx_ecfp_init_private_key_no_throw(CX_CURVE_256K1, privateKeyData, 32,
&privateKey));
explicit_bzero(privateKeyData, sizeof(privateKeyData));
unsigned int info = 0;
size_t sig_len = sizeof(signature);
io_seproxyhal_io_heartbeat();
cx_ecdsa_sign(&privateKey, CX_RND_RFC6979 | CX_LAST, CX_SHA256,
CX_THROW(cx_ecdsa_sign_no_throw(&privateKey, CX_RND_RFC6979 | CX_LAST, CX_SHA256,
tmpCtx.transactionContext.hash,
sizeof(tmpCtx.transactionContext.hash), signature, sizeof(signature), &info);
sizeof(tmpCtx.transactionContext.hash), signature, &sig_len, &info));
explicit_bzero(&privateKey, sizeof(privateKey));
// Parity is present in the sequence tag in the legacy API
if (tmpContent.txContent.vLength == 0) {
Expand Down Expand Up @@ -148,22 +149,24 @@ unsigned int io_seproxyhal_touch_tx_cancel(void) {
}

unsigned int io_seproxyhal_touch_signMessage_ok(void) {
uint8_t privateKeyData[32];
uint8_t privateKeyData[64];
uint8_t signature[100];
cx_ecfp_private_key_t privateKey;
uint32_t tx = 0;
io_seproxyhal_io_heartbeat();
os_perso_derive_node_bip32(
CX_CURVE_256K1, tmpCtx.messageSigningContext.derivationPath.path,
tmpCtx.messageSigningContext.derivationPath.len, privateKeyData, NULL);
CX_THROW(os_derive_bip32_no_throw(
CX_CURVE_256K1, tmpCtx.messageSigningContext.derivationPath.path,
tmpCtx.messageSigningContext.derivationPath.len, privateKeyData, NULL));

io_seproxyhal_io_heartbeat();
cx_ecfp_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);
CX_THROW(cx_ecfp_init_private_key_no_throw(CX_CURVE_256K1, privateKeyData, 32, &privateKey));
explicit_bzero(privateKeyData, sizeof(privateKeyData));
unsigned int info = 0;
size_t sig_len = sizeof(signature);
io_seproxyhal_io_heartbeat();
cx_ecdsa_sign(&privateKey, CX_RND_RFC6979 | CX_LAST, CX_SHA256,
CX_THROW(cx_ecdsa_sign_no_throw(&privateKey, CX_RND_RFC6979 | CX_LAST, CX_SHA256,
tmpCtx.messageSigningContext.hash,
sizeof(tmpCtx.messageSigningContext.hash), signature, sizeof(signature), &info);
sizeof(tmpCtx.messageSigningContext.hash), signature, &sig_len, &info));
explicit_bzero(&privateKey, sizeof(privateKey));
G_io_apdu_buffer[0] = 27;
if (info & CX_ECCINFO_PARITY_ODD) {
Expand Down
6 changes: 3 additions & 3 deletions src_common/ethUstream.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ static int readTxByte(txContext_t *context, uint8_t *byte) {
}
#ifndef TESTING
if (!(context->processingField && context->fieldSingleByte)) {
cx_hash((cx_hash_t*)context->sha3, 0, &data, 1, NULL, 0);
CX_THROW(cx_hash_no_throw((cx_hash_t*)context->sha3, 0, &data, 1, NULL, 0));
}
#endif
if (byte) {
Expand All @@ -59,7 +59,7 @@ int copyTxData(txContext_t *context, uint8_t *out, size_t length) {
}
#ifndef TESTING
if (!(context->processingField && context->fieldSingleByte)) {
cx_hash((cx_hash_t*)context->sha3, 0, context->workBuffer, length, NULL, 0);
CX_THROW(cx_hash_no_throw((cx_hash_t*)context->sha3, 0, context->workBuffer, length, NULL, 0));
}
#endif
context->workBuffer += length;
Expand Down Expand Up @@ -550,6 +550,6 @@ void initTx(txContext_t *context, cx_sha3_t *sha3, txContent_t *content,
context->extra = extra;
context->currentField = TX_RLP_CONTENT;
#ifndef TESTING
cx_keccak_init(context->sha3, 256);
CX_THROW(cx_keccak_init_no_throw(context->sha3, 256));
#endif
}
16 changes: 8 additions & 8 deletions src_common/ethUtils.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,17 @@ char convertDigit(uint8_t *address, uint8_t index, uint8_t *hash) {
void getEthAddressStringFromKey(const cx_ecfp_public_key_t *publicKey, char *out,
cx_sha3_t *sha3Context) {
uint8_t hashAddress[32];
cx_keccak_init(sha3Context, 256);
cx_hash((cx_hash_t*)sha3Context, CX_LAST, publicKey->W + 1, 64, hashAddress, 32);
CX_THROW(cx_keccak_init_no_throw(sha3Context, 256));
CX_THROW(cx_hash_no_throw((cx_hash_t*)sha3Context, CX_LAST, publicKey->W + 1, 64, hashAddress, 32));
getEthAddressStringFromBinary(hashAddress + 12, out, sha3Context);
}

void getEthAddressStringFromBinary(const uint8_t *address, uint8_t *out,
cx_sha3_t *sha3Context) {
uint8_t hashChecksum[32];
uint8_t i;
cx_keccak_init(sha3Context, 256);
cx_hash((cx_hash_t*)sha3Context, CX_LAST, address, 20, hashChecksum, 32);
CX_THROW(cx_keccak_init_no_throw(sha3Context, 256));
CX_THROW(cx_hash_no_throw((cx_hash_t*)sha3Context, CX_LAST, address, 20, hashChecksum, 32));
for (i = 0; i < 40; i++) {
out[i] = convertDigit(address, i, hashChecksum);
}
Expand All @@ -84,8 +84,8 @@ static const uint8_t HEXDIGITS[] = "0123456789abcdef";

void getEthAddressStringFromKey(const cx_ecfp_public_key_t *publicKey, char *out, int chainId, cx_sha3_t *sha3Context) {
uint8_t hashAddress[32];
cx_keccak_init(sha3Context, 256);
cx_hash((cx_hash_t*)sha3Context, CX_LAST, publicKey->W + 1, 64, hashAddress, 32);
CX_THROW(cx_keccak_init_no_throw(sha3Context, 256));
CX_THROW(cx_hash_no_throw((cx_hash_t*)sha3Context, CX_LAST, publicKey->W + 1, 64, hashAddress, 32));
getEthAddressStringFromBinary(hashAddress + 12, out, chainId, sha3Context);
}

Expand All @@ -111,8 +111,8 @@ void getEthAddressStringFromBinary(const uint8_t *address, char *out, int chainI
tmp[offset + 2 * i] = HEXDIGITS[(digit >> 4) & 0x0f];
tmp[offset + 2 * i + 1] = HEXDIGITS[digit & 0x0f];
}
cx_keccak_init(sha3Context, 256);
cx_hash((cx_hash_t*)sha3Context, CX_LAST, (uint8_t *) tmp, offset + 40, hashChecksum, 32);
CX_THROW(cx_keccak_init_no_throw(sha3Context, 256));
CX_THROW(cx_hash_no_throw((cx_hash_t*)sha3Context, CX_LAST, (uint8_t *) tmp, offset + 40, hashChecksum, 32));
for (i = 0; i < 40; i++) {
uint8_t digit = address[i / 2];
if ((i % 2) == 0) {
Expand Down
1 change: 1 addition & 0 deletions tests/python/apps/celo_utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from ragger.bip import pack_derivation_path

CELO_PACKED_DERIVATION_PATH = pack_derivation_path("m/44'/52752'/12345'")
ETH_PACKED_DERIVATION_PATH = pack_derivation_path("m/44'/60'/0'")
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading