-
Notifications
You must be signed in to change notification settings - Fork 9
/
modtcc-codecs.c
287 lines (230 loc) · 8.1 KB
/
modtcc-codecs.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/*
* Copyright (c) 2018 Coinkite Inc.
*
* Licensed under GNU License
* see LICENSE file for details
*
*
* Various encodes/decoders/serializers: base58, base32, bech32, etc.
*
*/
#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);
int rl = base58_encode_check(buf.buf, buf.len, HASHER_SHA2, 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)
uint8_t packed[tmp_len];
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,
};
// EOF