/*******************************************************************************
* Ark Wallet
* (c) 2017 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 <stdbool.h>
#include <string.h>
#include "arkHelpers.h"
#include "arkBase58.h"
void ark_public_key_hash160(unsigned char WIDE *in, unsigned short inlen,
unsigned char *out) {
union {
cx_sha256_t shasha;
cx_ripemd160_t riprip;
} u;
cx_ripemd160_init(&u.riprip);
#if defined(TARGET_NANOS)
cx_hash(&u.riprip.header, CX_LAST, in, inlen, out);
#endif
#if defined(TARGET_NANOX)
cx_hash(&u.riprip.header, CX_LAST, in, inlen, out, 32);
#endif
}
unsigned short ark_public_key_to_encoded_base58(unsigned char WIDE *in,
unsigned short inlen,
unsigned char *out,
unsigned short outlen,
unsigned short version,
unsigned char alreadyHashed) {
unsigned char tmpBuffer[26];
unsigned char checksumBuffer[32];
cx_sha256_t hash;
unsigned char versionSize = (version > 255 ? 2 : 1);
if (version > 255) {
tmpBuffer[0] = (version >> 8);
tmpBuffer[1] = version;
} else {
tmpBuffer[0] = version;
}
if (!alreadyHashed) {
ark_public_key_hash160(in, inlen, tmpBuffer + versionSize);
} else {
os_memmove(tmpBuffer + versionSize, in + versionSize, 20);
}
#if defined(TARGET_NANOS)
cx_sha256_init(&hash);
cx_hash(&hash.header, CX_LAST, tmpBuffer, 20 + versionSize, checksumBuffer);
cx_sha256_init(&hash);
cx_hash(&hash.header, CX_LAST, checksumBuffer, 32, checksumBuffer);
#endif
#if defined(TARGET_NANOX)
cx_sha256_init(&hash);
cx_hash(&hash.header, CX_LAST, tmpBuffer, 20 + versionSize, checksumBuffer, 32);
cx_sha256_init(&hash);
cx_hash(&hash.header, CX_LAST, checksumBuffer, 32, checksumBuffer, 32);
#endif
os_memmove(tmpBuffer + 20 + versionSize, checksumBuffer, 4);
return ark_encode_base58(tmpBuffer, 24 + versionSize, out, outlen);
}
unsigned short ark_address_to_encoded_base58(unsigned char WIDE *in,
unsigned short inlen,
unsigned char *out,
unsigned short outlen) {
unsigned char tmpBuffer[inlen+4];
unsigned char checksumBuffer[32];
cx_sha256_t hash;
#if defined(TARGET_NANOS)
cx_sha256_init(&hash);
cx_hash(&hash.header, CX_LAST, in, inlen, checksumBuffer);
cx_sha256_init(&hash);
cx_hash(&hash.header, CX_LAST, checksumBuffer, 32, checksumBuffer);
#endif
#if defined(TARGET_NANOX)
cx_sha256_init(&hash);
cx_hash(&hash.header, CX_LAST, in, inlen, checksumBuffer, 32);
cx_sha256_init(&hash);
cx_hash(&hash.header, CX_LAST, checksumBuffer, 32, checksumBuffer, 32);
#endif
os_memmove(tmpBuffer + inlen, checksumBuffer, 4);
return ark_encode_base58(tmpBuffer, inlen + 4, out, outlen);
}
unsigned short ark_decode_base58_address(unsigned char WIDE *in,
unsigned short inlen,
unsigned char *out,
unsigned short outlen) {
unsigned char hashBuffer[32];
cx_sha256_t hash;
outlen = ark_decode_base58(in, inlen, out, outlen);
#if defined(TARGET_NANOS)
// Compute hash to verify address
cx_sha256_init(&hash);
cx_hash(&hash.header, CX_LAST, out, outlen - 4, hashBuffer);
cx_sha256_init(&hash);
cx_hash(&hash.header, CX_LAST, hashBuffer, 32, hashBuffer);
#endif
#if defined(TARGET_NANOX)
cx_sha256_init(&hash);
cx_hash(&hash.header, CX_LAST, out, outlen - 4, hashBuffer, 32);
cx_sha256_init(&hash);
cx_hash(&hash.header, CX_LAST, hashBuffer, 32, hashBuffer, 32);
#endif
if (os_memcmp(out + outlen - 4, hashBuffer, 4)) {
THROW(INVALID_CHECKSUM);
}
return outlen;
}
unsigned short ark_compress_public_key(cx_ecfp_public_key_t *publicKey,
uint8_t *out, uint32_t outlen) {
if (outlen < 33) {
THROW(EXCEPTION_OVERFLOW);
}
if (publicKey->curve == CX_CURVE_256K1) {
out[0] = ((publicKey->W[64] & 1) ? 0x03 : 0x02);
os_memmove(out + 1, publicKey->W + 1, 32);
} else if (publicKey->curve == CX_CURVE_Ed25519) {
uint8_t i;
out[0] = 0xED;
for (i = 0; i < 32; i++) {
out[i + 1] = publicKey->W[64 - i];
}
if ((publicKey->W[32] & 1) != 0) {
out[32] |= 0x80;
}
} else {
THROW(EXCEPTION);
}
return 33;
}
#if 0
unsigned short ark_print_amount(uint64_t amount, uint8_t *out, uint32_t outlen) {
uint64_t partInt;
uint64_t partDecimal;
partInt = amount / 1000000;
partDecimal = amount - (partInt * 1000000);
// TODO : handle properly
if ((partInt > 0xFFFFFFFF) || (partDecimal > 0xFFFFFFFF)) {
THROW(EXCEPTION);
}
if (partDecimal == 0) {
snprintf(out, outlen, "%d", (uint32_t)partInt);
}
else {
snprintf(out, outlen, "%d.%d", (uint32_t)partInt, (uint32_t)partDecimal);
}
return strlen(out);
}
#endif
bool adjustDecimals(char *src, uint32_t srcLength, char *target,
uint32_t targetLength, uint8_t decimals) {
uint32_t startOffset;
uint32_t lastZeroOffset = 0;
uint32_t offset = 0;
if ((srcLength == 1) && (*src == '0')) {
if (targetLength < 2) {
return false;
}
target[offset++] = '0';
target[offset++] = '\0';
return true;
}
if (srcLength <= decimals) {
uint32_t delta = decimals - srcLength;
if (targetLength < srcLength + 1 + 2 + delta) {
return false;
}
target[offset++] = '0';
target[offset++] = '.';
for (uint32_t i = 0; i < delta; i++) {
target[offset++] = '0';
}
startOffset = offset;
for (uint32_t i = 0; i < srcLength; i++) {
target[offset++] = src[i];
}
target[offset] = '\0';
} else {
uint32_t sourceOffset = 0;
uint32_t delta = srcLength - decimals;
if (targetLength < srcLength + 1 + 1) {
return false;
}
while (offset < delta) {
target[offset++] = src[sourceOffset++];
}
if (decimals != 0) {
target[offset++] = '.';
}
startOffset = offset;
while (sourceOffset < srcLength) {
target[offset++] = src[sourceOffset++];
}
target[offset] = '\0';
}
for (uint32_t i = startOffset; i < offset; i++) {
if (target[i] == '0') {
if (lastZeroOffset == 0) {
lastZeroOffset = i;
}
} else {
lastZeroOffset = 0;
}
}
if (lastZeroOffset != 0) {
target[lastZeroOffset] = '\0';
if (target[lastZeroOffset - 1] == '.') {
target[lastZeroOffset - 1] = '\0';
}
}
return true;
}
unsigned short ark_print_amount(uint64_t amount, uint8_t *out,
uint32_t outlen) {
uint8_t tmp[20] = { '\0' };
uint8_t tmp2[25] = { '\0' };
uint32_t numDigits = 0, i;
uint64_t base = 1;
while (base <= amount) {
base *= 10;
numDigits++;
}
if (numDigits > sizeof(tmp) - 1) {
THROW(EXCEPTION);
}
base /= 10;
for (i = 0; i < numDigits; i++) {
tmp[i] = '0' + ((amount / base) % 10);
base /= 10;
}
tmp[i] = '\0';
os_memmove(tmp2, "ARK ", 5);
adjustDecimals((char *)tmp, i, (char *)tmp2 + 4, 25, 8);
size_t tmp2Len = strlen((char *)tmp2);
if (tmp2Len < outlen - 1) {
os_memmove(out, tmp2, tmp2Len);
} else {
out[0] = '\0';
}
return strlen((char *)out);
}