Skip to content

Instantly share code, notes, and snippets.

@sleepdefic1t
Created April 1, 2020 01:02
Show Gist options
  • Save sleepdefic1t/4a47cd263203d02884c7c912c916f612 to your computer and use it in GitHub Desktop.
Save sleepdefic1t/4a47cd263203d02884c7c912c916f612 to your computer and use it in GitHub Desktop.
base58 w/address/pubkeyhash
/*******************************************************************************
* This file is part of the ARK Ledger App.
*
* Copyright (c) ARK Ecosystem <[email protected]>
*
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* -----
*
* Parts of this software are based on the Ledger Bitcoin Wallet App
*
* (btchip_encode_base58)
* - src: https://github.com/LedgerHQ/ledger-app-btc/blob/master/src/btchip_base58.c#L79
*
* changes:
* - rename 'btchip_encode_base58' -> 'Base58Encode'.
* - moved global 'MAX' variable to local scope.
* - added more checks.
* - added U suffix to integer literals.
*
* Ledger App - Bitcoin Wallet
* (c) 2016-2019 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 "utils/base58.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "constants.h"
#include "crypto/hashing.h"
#include "utils/utils.h"
// for more information on Base58 Encoding:
// - https://en.bitcoin.it/wiki/Base58Check_encoding
////////////////////////////////////////////////////////////////////////////////
// Length of the Checksum appended to a Base58Check encoded string.
static const size_t BASE58_CHECKSUM_LEN = 4U;
////////////////////////////////////////////////////////////////////////////////
// Base58 Alphabet Table
static const uint8_t BASE58ALPHABET[] = {
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
};
////////////////////////////////////////////////////////////////////////////////
// Base58 Encode an input stream. Outputs a formatted string.
//
// @param const unsigned char *in: input byte-array to be encoded.
// @param size_t length: length of the input.
// @param unsigned char *out: destination where output will be written.
// @param size_t *outlen: ptr to the max length of writable space.
//
// @return int: '0' if successful, otherwise '-1'.
//
// ---
int Base58Encode(const uint8_t *in, size_t length,
char *out, size_t *outlen) {
const size_t MAX_ENC_INPUT_SIZE = 120U;
if (in == NULL || length == 0U ||
out == NULL || *out < 0U || outlen == 0U ||
length > MAX_ENC_INPUT_SIZE) {
return -1;
}
size_t i = 0U, j;
size_t startAt, stopAt;
size_t zeroCount = 0U;
size_t outputSize;
while ((zeroCount < length) && (in[zeroCount] == 0U)) {
++zeroCount;
}
unsigned char buffer[MAX_ENC_INPUT_SIZE * 138U / 100U + 1U] = { 0U };
outputSize = (length - zeroCount) * 138U / 100U + 1U;
stopAt = outputSize - 1U;
for (startAt = zeroCount; startAt < length; startAt++) {
int carry = in[startAt];
for (j = outputSize - 1U; (int)j >= 0U; j--) {
carry += 256U * buffer[j];
buffer[j] = carry % 58U;
carry /= 58U;
if (j <= stopAt - 1U && carry == 0U) {
break;
}
}
stopAt = j;
}
j = 0U;
while (j < outputSize && buffer[j] == 0U) {
j += 1U;
}
if (*outlen < zeroCount + outputSize - j) {
*outlen = zeroCount + outputSize - j;
return -1;
}
MEMSET(out, BASE58ALPHABET[0], zeroCount);
i = zeroCount;
while (j < outputSize) {
out[i++] = BASE58ALPHABET[buffer[j++]];
}
*outlen = i;
return 0U;
}
////////////////////////////////////////////////////////////////////////////////
// Base58Check Encode an input stream, Outputs a formatted string.
//
// @param uint8_t *in: input byte-array to be encoded.
// @param size_t length: length of the input.
// @param unsigned char *out: destination where output will be written.
// @param size_t *outlen: ptr to the max length of writable space.
//
// @return int: '0' if successful, otherwise '-1'.
//
// --
int Base58CheckEncode(const uint8_t *in, size_t length,
char *out, size_t *outLen) {
if (in == NULL || length == 0U || out == NULL || outLen == NULL) {
return -1;
}
const size_t finalLen = length + BASE58_CHECKSUM_LEN;
if (finalLen > *outLen) {
out[0] = '\0';
*outLen = -1;
return -1;
}
uint8_t temp[finalLen];
uint8_t checksum[HASH_32_LEN];
MEMCOPY(temp, in, length);
// Calculate the checksum and append the first 4 bytes to the output.
hash256(temp, length, checksum);
hash256(checksum, HASH_32_LEN, checksum);
MEMCOPY(&temp[length], checksum, BASE58_CHECKSUM_LEN);
MEMSET_BZERO(checksum, HASH_32_LEN);
if (Base58Encode(temp, finalLen, out, outLen) < 0) {
MEMSET_BZERO(temp, finalLen);
MEMSET_BZERO(out, *outLen);
return -1;
}
MEMSET_BZERO(temp, finalLen);
return 0U;
}
////////////////////////////////////////////////////////////////////////////////
// Derive an Address from a PublicKey or PubkeyHash, given a Network Version.
//
// @param const uint8_t *in: the publicKey or pubkeyHash byte-array.
// @param size_t length: length of the publicKey or pubkeyHash input.
// @param char *out: destination where address will be written.
// @param size_t outLen: max length of writable space.
// @param uint16_t version: network version for address derivation.
// @param uint8_t needsHashed: whether the input needs Ripemd160 hashing.
//
//
// @return int: '0' if successful, otherwise '-1'.
//
// ---
int AddressFromPublicKey(const uint8_t *in, size_t length,
char *out, size_t outLen,
uint8_t version,
uint8_t needsHashed) {
if (in == NULL || length == 0U || out == NULL || outLen == 0U) {
return -1;
}
uint8_t temp[1U + HASH_20_LEN];
temp[0] = version;
if (needsHashed) {
// Create a Ripemd160 PubkeyHash of the publicKey.
hash160((uint8_t *)in, length, &temp[1]);
}
else {
// already hashed, just copy the PubkeyHash for Base58Check encoding.
MEMCOPY(&temp[1], &in[1], HASH_20_LEN);
}
return Base58CheckEncode(temp, 1U + HASH_20_LEN, out, &outLen);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment