You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
323 lines
9.0 KiB
323 lines
9.0 KiB
// SPDX-FileCopyrightText: 2020 Foundation Devices, Inc. <hello@foundationdevices.com>
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
// SPDX-FileCopyrightText: 2018 Coinkite, Inc. <coldcardwallet.com>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
//
|
|
/*
|
|
* Copyright (c) 2018 Coinkite Inc.
|
|
*
|
|
* Licensed under GNU License
|
|
* see LICENSE file for details
|
|
*
|
|
*
|
|
* Various encodes/decoders/serializers: base58, base32, bech32, etc.
|
|
*
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
|
|
#include "py/runtime.h"
|
|
|
|
#include "py/objstr.h"
|
|
|
|
#include "hasher.h"
|
|
#include "base58.h"
|
|
#include "base32.h"
|
|
#include "segwit_addr.h"
|
|
|
|
/*
|
|
int base58_encode_check(const uint8_t *data, int len, HasherType hasher_type, char *str, int strsize);
|
|
int base58_decode_check(const char *str, HasherType hasher_type, uint8_t *data, int datalen);
|
|
|
|
char *base32_encode(const uint8_t *in, size_t inlen, char *out, size_t outlen, const char *alphabet);
|
|
uint8_t *base32_decode(const char *in, size_t inlen, uint8_t *out, size_t outlen, const char *alphabet);
|
|
*/
|
|
|
|
//
|
|
// Base 58
|
|
//
|
|
|
|
STATIC mp_obj_t modtcc_b58_encode(mp_obj_t data)
|
|
{
|
|
mp_buffer_info_t buf;
|
|
mp_get_buffer_raise(data, &buf, MP_BUFFER_READ);
|
|
if (buf.len == 0) {
|
|
// there is an encoding for empty string (4 bytes of fixed checksum) but not useful
|
|
mp_raise_ValueError(NULL);
|
|
}
|
|
|
|
vstr_t vstr;
|
|
vstr_init_len(&vstr, (buf.len*2)+10);
|
|
|
|
// Do double SHA on the hash
|
|
int rl = base58_encode_check(buf.buf, buf.len, HASHER_SHA2D, vstr.buf, vstr.len);
|
|
|
|
if(rl < 1) {
|
|
// unlikely
|
|
mp_raise_ValueError(NULL);
|
|
}
|
|
|
|
vstr.len = rl-1; // strip NUL
|
|
|
|
return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(modtcc_b58_encode_obj, modtcc_b58_encode);
|
|
|
|
STATIC mp_obj_t modtcc_b58_decode(mp_obj_t enc)
|
|
{
|
|
const char *s = mp_obj_str_get_str(enc);
|
|
|
|
uint8_t tmp[128];
|
|
|
|
int rl = base58_decode_check(s, HASHER_SHA2, tmp, sizeof(tmp));
|
|
|
|
if(rl <= 0) {
|
|
// transcription error from user is very likely
|
|
mp_raise_ValueError("corrupt base58");
|
|
}
|
|
|
|
return mp_obj_new_bytes(tmp, rl);
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(modtcc_b58_decode_obj, modtcc_b58_decode);
|
|
|
|
//
|
|
// Base 32
|
|
//
|
|
|
|
STATIC mp_obj_t modtcc_b32_encode(mp_obj_t data)
|
|
{
|
|
mp_buffer_info_t buf;
|
|
mp_get_buffer_raise(data, &buf, MP_BUFFER_READ);
|
|
if (buf.len == 0) {
|
|
return mp_const_empty_bytes;
|
|
}
|
|
|
|
vstr_t vstr;
|
|
vstr_init_len(&vstr, (buf.len*2)+10);
|
|
|
|
char *last = base32_encode(buf.buf, buf.len, vstr.buf, vstr.len, BASE32_ALPHABET_RFC4648);
|
|
|
|
if(!last) {
|
|
// unlikely
|
|
mp_raise_ValueError(NULL);
|
|
}
|
|
|
|
vstr.len = last - vstr.buf; // strips NUL
|
|
|
|
return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(modtcc_b32_encode_obj, modtcc_b32_encode);
|
|
|
|
STATIC mp_obj_t modtcc_b32_decode(mp_obj_t enc)
|
|
{
|
|
const char *s = mp_obj_str_get_str(enc);
|
|
|
|
uint8_t tmp[256];
|
|
|
|
uint8_t *last = base32_decode(s, strlen(s), tmp, sizeof(tmp), BASE32_ALPHABET_RFC4648);
|
|
|
|
if(!last) {
|
|
// transcription error from user is very likely
|
|
mp_raise_ValueError("corrupt base32");
|
|
}
|
|
|
|
return mp_obj_new_bytes(tmp, last-tmp);
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(modtcc_b32_decode_obj, modtcc_b32_decode);
|
|
|
|
//
|
|
//
|
|
// Bech32 aka. Segwit addresses, but hopefylly not specific to segwit addresses only.
|
|
//
|
|
//
|
|
|
|
// pack and unpack bits; probably 5 or 8...
|
|
//
|
|
STATIC inline int
|
|
sw_convert_bits(
|
|
uint8_t* out, size_t* outlen,
|
|
const int outbits,
|
|
const uint8_t* in, size_t inlen,
|
|
const int inbits, bool pad
|
|
) {
|
|
uint32_t val = 0;
|
|
int bits = 0;
|
|
uint32_t maxv = (((uint32_t)1) << outbits) - 1;
|
|
while (inlen--) {
|
|
val = (val << inbits) | *(in++);
|
|
bits += inbits;
|
|
while (bits >= outbits) {
|
|
bits -= outbits;
|
|
out[(*outlen)++] = (val >> bits) & maxv;
|
|
}
|
|
}
|
|
if (pad) {
|
|
if (bits) {
|
|
out[(*outlen)++] = (val << (outbits - bits)) & maxv;
|
|
}
|
|
} else if (((val << (outbits - bits)) & maxv) || bits >= inbits) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
STATIC mp_obj_t modtcc_bech32_encode(mp_obj_t hrp_obj, mp_obj_t segwit_version_obj, mp_obj_t data_obj)
|
|
{
|
|
const char *hrp = mp_obj_str_get_str(hrp_obj);
|
|
uint32_t segwit_version = mp_obj_int_get_checked(segwit_version_obj);
|
|
|
|
mp_buffer_info_t buf;
|
|
mp_get_buffer_raise(data_obj, &buf, MP_BUFFER_READ);
|
|
|
|
// low-level bech32 functions want 5-bit data unpacked into bytes. first value is
|
|
// the version number (5 bits), and remainder is packed data.
|
|
|
|
if(segwit_version > 16) {
|
|
mp_raise_ValueError("sw version");
|
|
}
|
|
|
|
uint8_t data[buf.len + 1];
|
|
size_t data_len = 0;
|
|
data[0] = segwit_version;
|
|
int cv_ok = sw_convert_bits(data + 1, &data_len, 5, buf.buf, buf.len, 8, true);
|
|
|
|
if(cv_ok != 1) {
|
|
mp_raise_ValueError("pack fail");
|
|
}
|
|
|
|
// we already prefixed the version number
|
|
data_len += 1;
|
|
|
|
vstr_t vstr;
|
|
vstr_init_len(&vstr, strlen(hrp) + data_len + 8);
|
|
|
|
/** Encode a Bech32 string
|
|
*
|
|
* Out: output: Pointer to a buffer of size strlen(hrp) + data_len + 8 that
|
|
* will be updated to contain the null-terminated Bech32 string.
|
|
* In: hrp : Pointer to the null-terminated human readable part.
|
|
* data : Pointer to an array of 5-bit values.
|
|
* data_len: Length of the data array.
|
|
* Returns 1 if successful.
|
|
|
|
int bech32_encode(
|
|
char *output,
|
|
const char *hrp,
|
|
const uint8_t *data,
|
|
size_t data_len);
|
|
*/
|
|
int rv = bech32_encode(vstr.buf, hrp, data, data_len);
|
|
|
|
if(rv != 1) {
|
|
mp_raise_ValueError("encode fail");
|
|
}
|
|
|
|
vstr.len = strlen(vstr.buf);
|
|
|
|
return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_3(modtcc_bech32_encode_obj, modtcc_bech32_encode);
|
|
|
|
STATIC mp_obj_t modtcc_bech32_decode(mp_obj_t enc)
|
|
{
|
|
const char *s = mp_obj_str_get_str(enc);
|
|
|
|
/** Decode a Bech32 string
|
|
*
|
|
* Out: hrp: Pointer to a buffer of size strlen(input) - 6. Will be
|
|
* updated to contain the null-terminated human readable part.
|
|
* data: Pointer to a buffer of size strlen(input) - 8 that will
|
|
* hold the encoded 5-bit data values.
|
|
* data_len: Pointer to a size_t that will be updated to be the number
|
|
* of entries in data.
|
|
* In: input: Pointer to a null-terminated Bech32 string.
|
|
* Returns 1 if succesful.
|
|
int bech32_decode(
|
|
char *hrp,
|
|
uint8_t *data,
|
|
size_t *data_len,
|
|
const char *input
|
|
);
|
|
*/
|
|
|
|
char hrp[strlen(s) + 16];
|
|
uint8_t tmp[strlen(s) + 16]; // actually 8-bit
|
|
size_t tmp_len = 0;
|
|
|
|
int rv = bech32_decode(hrp, tmp, &tmp_len, s);
|
|
|
|
if(rv != 1) {
|
|
// probably transcription error from user
|
|
mp_raise_ValueError("corrupt bech32");
|
|
}
|
|
|
|
if(tmp_len <= 1) {
|
|
// lots of valid Bech32 strings, but invalid for segwit puposes
|
|
// can end up here; but don't care.
|
|
mp_raise_ValueError("no sw verion and/or data");
|
|
}
|
|
|
|
// re-pack 5-bit data into 8-bit bytes (after version)
|
|
#ifndef __APPLE__
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wvla-larger-than="
|
|
#endif
|
|
uint8_t packed[tmp_len];
|
|
#ifndef __APPLE__
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
size_t packed_len = 0;
|
|
|
|
int cv_ok = sw_convert_bits(packed, &packed_len, 8, tmp + 1, tmp_len - 1, 5, false);
|
|
|
|
if(cv_ok != 1) {
|
|
mp_raise_ValueError("repack fail");
|
|
}
|
|
|
|
// return a tuple: (hrp, version, data)
|
|
mp_obj_t tuple[3] = {
|
|
mp_obj_new_str(hrp, strlen(hrp)),
|
|
MP_OBJ_NEW_SMALL_INT(tmp[0]),
|
|
mp_obj_new_bytes(packed, packed_len),
|
|
};
|
|
|
|
return mp_obj_new_tuple(3, tuple);
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(modtcc_bech32_decode_obj, modtcc_bech32_decode);
|
|
|
|
STATIC const mp_rom_map_elem_t modtcc_codecs_globals_table[] = {
|
|
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_codecs) },
|
|
{ MP_ROM_QSTR(MP_QSTR_b58_encode), MP_ROM_PTR(&modtcc_b58_encode_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_b58_decode), MP_ROM_PTR(&modtcc_b58_decode_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_b32_encode), MP_ROM_PTR(&modtcc_b32_encode_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_b32_decode), MP_ROM_PTR(&modtcc_b32_decode_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_bech32_encode), MP_ROM_PTR(&modtcc_bech32_encode_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_bech32_decode), MP_ROM_PTR(&modtcc_bech32_decode_obj) },
|
|
};
|
|
STATIC MP_DEFINE_CONST_DICT(modtcc_codecs_globals, modtcc_codecs_globals_table);
|
|
|
|
STATIC const mp_obj_module_t modtcc_codecs_module = {
|
|
.base = { &mp_type_module },
|
|
.globals = (mp_obj_dict_t*)&modtcc_codecs_globals,
|
|
};
|
|
|
|
|
|
// Top level
|
|
|
|
STATIC const mp_rom_map_elem_t mp_module_tcc_globals_table[] = {
|
|
{ MP_ROM_QSTR(MP_QSTR_codecs), MP_ROM_PTR(&modtcc_codecs_module) },
|
|
};
|
|
STATIC MP_DEFINE_CONST_DICT(mp_module_tcc_globals, mp_module_tcc_globals_table);
|
|
|
|
const mp_obj_module_t mp_module_tcc = {
|
|
.base = { &mp_type_module },
|
|
.globals = (mp_obj_dict_t*)&mp_module_tcc_globals,
|
|
};
|
|
|
|
MP_REGISTER_MODULE(MP_QSTR_tcc, mp_module_tcc, 1);
|
|
|
|
// EOF
|
|
|