1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2014 Paul Sokolovsky
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  */
26 
27 #include <stdio.h>
28 #include <assert.h>
29 #include <string.h>
30 
31 #include "py/runtime.h"
32 #include "py/binary.h"
33 
34 #if MICROPY_PY_UBINASCII
35 
mod_binascii_hexlify(size_t n_args,const mp_obj_t * args)36 STATIC mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args) {
37     // First argument is the data to convert.
38     // Second argument is an optional separator to be used between values.
39     const char *sep = NULL;
40     mp_buffer_info_t bufinfo;
41     mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
42 
43     // Code below assumes non-zero buffer length when computing size with
44     // separator, so handle the zero-length case here.
45     if (bufinfo.len == 0) {
46         return mp_const_empty_bytes;
47     }
48 
49     vstr_t vstr;
50     size_t out_len = bufinfo.len * 2;
51     if (n_args > 1) {
52         // 1-char separator between hex numbers
53         out_len += bufinfo.len - 1;
54         sep = mp_obj_str_get_str(args[1]);
55     }
56     vstr_init_len(&vstr, out_len);
57     byte *in = bufinfo.buf, *out = (byte *)vstr.buf;
58     for (mp_uint_t i = bufinfo.len; i--;) {
59         byte d = (*in >> 4);
60         if (d > 9) {
61             d += 'a' - '9' - 1;
62         }
63         *out++ = d + '0';
64         d = (*in++ & 0xf);
65         if (d > 9) {
66             d += 'a' - '9' - 1;
67         }
68         *out++ = d + '0';
69         if (sep != NULL && i != 0) {
70             *out++ = *sep;
71         }
72     }
73     return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
74 }
75 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_hexlify_obj, 1, 2, mod_binascii_hexlify);
76 
mod_binascii_unhexlify(mp_obj_t data)77 STATIC mp_obj_t mod_binascii_unhexlify(mp_obj_t data) {
78     mp_buffer_info_t bufinfo;
79     mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
80 
81     if ((bufinfo.len & 1) != 0) {
82         mp_raise_ValueError(MP_ERROR_TEXT("odd-length string"));
83     }
84     vstr_t vstr;
85     vstr_init_len(&vstr, bufinfo.len / 2);
86     byte *in = bufinfo.buf, *out = (byte *)vstr.buf;
87     byte hex_byte = 0;
88     for (mp_uint_t i = bufinfo.len; i--;) {
89         byte hex_ch = *in++;
90         if (unichar_isxdigit(hex_ch)) {
91             hex_byte += unichar_xdigit_value(hex_ch);
92         } else {
93             mp_raise_ValueError(MP_ERROR_TEXT("non-hex digit found"));
94         }
95         if (i & 1) {
96             hex_byte <<= 4;
97         } else {
98             *out++ = hex_byte;
99             hex_byte = 0;
100         }
101     }
102     return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
103 }
104 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_unhexlify_obj, mod_binascii_unhexlify);
105 
106 // If ch is a character in the base64 alphabet, and is not a pad character, then
107 // the corresponding integer between 0 and 63, inclusively, is returned.
108 // Otherwise, -1 is returned.
mod_binascii_sextet(byte ch)109 static int mod_binascii_sextet(byte ch) {
110     if (ch >= 'A' && ch <= 'Z') {
111         return ch - 'A';
112     } else if (ch >= 'a' && ch <= 'z') {
113         return ch - 'a' + 26;
114     } else if (ch >= '0' && ch <= '9') {
115         return ch - '0' + 52;
116     } else if (ch == '+') {
117         return 62;
118     } else if (ch == '/') {
119         return 63;
120     } else {
121         return -1;
122     }
123 }
124 
mod_binascii_a2b_base64(mp_obj_t data)125 STATIC mp_obj_t mod_binascii_a2b_base64(mp_obj_t data) {
126     mp_buffer_info_t bufinfo;
127     mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
128     byte *in = bufinfo.buf;
129 
130     vstr_t vstr;
131     vstr_init(&vstr, (bufinfo.len / 4) * 3 + 1); // Potentially over-allocate
132     byte *out = (byte *)vstr.buf;
133 
134     uint shift = 0;
135     int nbits = 0; // Number of meaningful bits in shift
136     bool hadpad = false; // Had a pad character since last valid character
137     for (size_t i = 0; i < bufinfo.len; i++) {
138         if (in[i] == '=') {
139             if ((nbits == 2) || ((nbits == 4) && hadpad)) {
140                 nbits = 0;
141                 break;
142             }
143             hadpad = true;
144         }
145 
146         int sextet = mod_binascii_sextet(in[i]);
147         if (sextet == -1) {
148             continue;
149         }
150         hadpad = false;
151         shift = (shift << 6) | sextet;
152         nbits += 6;
153 
154         if (nbits >= 8) {
155             nbits -= 8;
156             out[vstr.len++] = (shift >> nbits) & 0xFF;
157         }
158     }
159 
160     if (nbits) {
161         mp_raise_ValueError(MP_ERROR_TEXT("incorrect padding"));
162     }
163 
164     return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
165 }
166 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_a2b_base64_obj, mod_binascii_a2b_base64);
167 
mod_binascii_b2a_base64(mp_obj_t data)168 STATIC mp_obj_t mod_binascii_b2a_base64(mp_obj_t data) {
169     mp_buffer_info_t bufinfo;
170     mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_READ);
171 
172     vstr_t vstr;
173     vstr_init_len(&vstr, ((bufinfo.len != 0) ? (((bufinfo.len - 1) / 3) + 1) * 4 : 0) + 1);
174 
175     // First pass, we convert input buffer to numeric base 64 values
176     byte *in = bufinfo.buf, *out = (byte *)vstr.buf;
177     mp_uint_t i;
178     for (i = bufinfo.len; i >= 3; i -= 3) {
179         *out++ = (in[0] & 0xFC) >> 2;
180         *out++ = (in[0] & 0x03) << 4 | (in[1] & 0xF0) >> 4;
181         *out++ = (in[1] & 0x0F) << 2 | (in[2] & 0xC0) >> 6;
182         *out++ = in[2] & 0x3F;
183         in += 3;
184     }
185     if (i != 0) {
186         *out++ = (in[0] & 0xFC) >> 2;
187         if (i == 2) {
188             *out++ = (in[0] & 0x03) << 4 | (in[1] & 0xF0) >> 4;
189             *out++ = (in[1] & 0x0F) << 2;
190         } else {
191             *out++ = (in[0] & 0x03) << 4;
192             *out++ = 64;
193         }
194         *out = 64;
195     }
196 
197     // Second pass, we convert number base 64 values to actual base64 ascii encoding
198     out = (byte *)vstr.buf;
199     for (mp_uint_t j = vstr.len - 1; j--;) {
200         if (*out < 26) {
201             *out += 'A';
202         } else if (*out < 52) {
203             *out += 'a' - 26;
204         } else if (*out < 62) {
205             *out += '0' - 52;
206         } else if (*out == 62) {
207             *out = '+';
208         } else if (*out == 63) {
209             *out = '/';
210         } else {
211             *out = '=';
212         }
213         out++;
214     }
215     *out = '\n';
216     return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
217 }
218 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_binascii_b2a_base64_obj, mod_binascii_b2a_base64);
219 
220 #if MICROPY_PY_UBINASCII_CRC32
221 #include "lib/uzlib/tinf.h"
222 
mod_binascii_crc32(size_t n_args,const mp_obj_t * args)223 STATIC mp_obj_t mod_binascii_crc32(size_t n_args, const mp_obj_t *args) {
224     mp_buffer_info_t bufinfo;
225     mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
226     uint32_t crc = (n_args > 1) ? mp_obj_get_int_truncated(args[1]) : 0;
227     crc = uzlib_crc32(bufinfo.buf, bufinfo.len, crc ^ 0xffffffff);
228     return mp_obj_new_int_from_uint(crc ^ 0xffffffff);
229 }
230 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_binascii_crc32_obj, 1, 2, mod_binascii_crc32);
231 #endif
232 
233 STATIC const mp_rom_map_elem_t mp_module_binascii_globals_table[] = {
234     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ubinascii) },
235     { MP_ROM_QSTR(MP_QSTR_hexlify), MP_ROM_PTR(&mod_binascii_hexlify_obj) },
236     { MP_ROM_QSTR(MP_QSTR_unhexlify), MP_ROM_PTR(&mod_binascii_unhexlify_obj) },
237     { MP_ROM_QSTR(MP_QSTR_a2b_base64), MP_ROM_PTR(&mod_binascii_a2b_base64_obj) },
238     { MP_ROM_QSTR(MP_QSTR_b2a_base64), MP_ROM_PTR(&mod_binascii_b2a_base64_obj) },
239     #if MICROPY_PY_UBINASCII_CRC32
240     { MP_ROM_QSTR(MP_QSTR_crc32), MP_ROM_PTR(&mod_binascii_crc32_obj) },
241     #endif
242 };
243 
244 STATIC MP_DEFINE_CONST_DICT(mp_module_binascii_globals, mp_module_binascii_globals_table);
245 
246 const mp_obj_module_t mp_module_ubinascii = {
247     .base = { &mp_type_module },
248     .globals = (mp_obj_dict_t *)&mp_module_binascii_globals,
249 };
250 
251 #endif // MICROPY_PY_UBINASCII
252