Skip to content

Commit 30b08d6

Browse files
committed
Move some common HEX functions into a static HEX class, remove those from MD5 and add some examples. This allows for the cleanup of various to/from HEX routines elsewhere.
1 parent f1282fc commit 30b08d6

File tree

6 files changed

+282
-17
lines changed

6 files changed

+282
-17
lines changed

cores/esp32/HEXBuilder.cpp

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
3+
This file is part of the esp8266 core for Arduino environment.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
#include <Arduino.h>
20+
#include <HEXBuilder.h>
21+
22+
static uint8_t hex_char_to_byte(uint8_t c)
23+
{
24+
return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa)) :
25+
(c >= 'A' && c <= 'F') ? (c - ((uint8_t)'A' - 0xA)) :
26+
(c >= '0' && c<= '9') ? (c - (uint8_t)'0') : 0x10; // unknown char is 16
27+
}
28+
29+
size_t HEXBuilder::hex2bytes(unsigned char * out, size_t maxlen, String &in) {
30+
return hex2bytes(out, maxlen, in.c_str());
31+
}
32+
33+
size_t HEXBuilder::hex2bytes(unsigned char * out, size_t maxlen, const char * in) {
34+
size_t len = 0;
35+
for(;*in;in++) {
36+
uint8_t c = hex_char_to_byte(*in);
37+
// Silently skip anything unknown.
38+
if (c > 15)
39+
continue;
40+
41+
if (len & 1) {
42+
if (len/2 < maxlen)
43+
out[len/2] |= c;
44+
} else {
45+
if (len/2 < maxlen)
46+
out[len/2] = c<<4;
47+
}
48+
len++;
49+
}
50+
return (len + 1)/2;
51+
}
52+
53+
size_t HEXBuilder::bytes2hex(char * out, size_t maxlen, const unsigned char * in, size_t len) {
54+
for(size_t i = 0; i < len; i++)
55+
if (i*2 + 1 < maxlen)
56+
sprintf(out + (i * 2), "%02x", in[i]);
57+
58+
return len * 2 + 1;
59+
}
60+
61+
String HEXBuilder::bytes2hex(const unsigned char * in, size_t len) {
62+
size_t maxlen = len * 2 + 1;
63+
char * out = (char *) malloc(maxlen);
64+
if (!out) return String();
65+
bytes2hex(out, maxlen, in, len);
66+
String ret = String(out);
67+
free(out);
68+
return ret;
69+
}

cores/esp32/HEXBuilder.h

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
Copyright (c) 2015 Hristo Gochkov. All rights reserved.
3+
This file is part of the esp8266 core for Arduino environment.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
#ifndef __ESP8266_HEX_BUILDER__
20+
#define __ESP8266_HEX_BUILDER__
21+
22+
#include <WString.h>
23+
#include <Stream.h>
24+
25+
class HEXBuilder {
26+
public:
27+
static size_t hex2bytes(unsigned char * out, size_t maxlen, String & in);
28+
static size_t hex2bytes(unsigned char * out, size_t maxlen, const char * in);
29+
30+
static String bytes2hex(const unsigned char * in, size_t len);
31+
static size_t bytes2hex(char * out, size_t maxlen, const unsigned char * in, size_t len);
32+
};
33+
#endif

cores/esp32/MD5Builder.cpp

+4-16
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,9 @@
1717
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1818
*/
1919
#include <Arduino.h>
20+
#include <HEXBuilder.h>
2021
#include <MD5Builder.h>
2122

22-
static uint8_t hex_char_to_byte(uint8_t c)
23-
{
24-
return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa)) :
25-
(c >= 'A' && c <= 'F') ? (c - ((uint8_t)'A' - 0xA)) :
26-
(c >= '0' && c<= '9') ? (c - (uint8_t)'0') : 0;
27-
}
28-
2923
void MD5Builder::begin(void)
3024
{
3125
memset(_buf, 0x00, ESP_ROM_MD5_DIGEST_LEN);
@@ -39,16 +33,12 @@ void MD5Builder::add(uint8_t * data, uint16_t len)
3933

4034
void MD5Builder::addHexString(const char * data)
4135
{
42-
uint16_t i, len = strlen(data);
36+
uint16_t len = strlen(data);
4337
uint8_t * tmp = (uint8_t*)malloc(len/2);
4438
if(tmp == NULL) {
4539
return;
4640
}
47-
for(i=0; i<len; i+=2) {
48-
uint8_t high = hex_char_to_byte(data[i]);
49-
uint8_t low = hex_char_to_byte(data[i+1]);
50-
tmp[i/2] = (high & 0x0F) << 4 | (low & 0x0F);
51-
}
41+
hex2bytes(tmp, len/2, data);
5242
add(tmp, len/2);
5343
free(tmp);
5444
}
@@ -104,9 +94,7 @@ void MD5Builder::getBytes(uint8_t * output)
10494

10595
void MD5Builder::getChars(char * output)
10696
{
107-
for(uint8_t i = 0; i < ESP_ROM_MD5_DIGEST_LEN; i++) {
108-
sprintf(output + (i * 2), "%02x", _buf[i]);
109-
}
97+
bytes2hex(output, ESP_ROM_MD5_DIGEST_LEN*2+1, _buf, ESP_ROM_MD5_DIGEST_LEN);
11098
}
11199

112100
String MD5Builder::toString(void)

cores/esp32/MD5Builder.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
#include "esp_system.h"
2626
#include "esp_rom_md5.h"
2727

28-
class MD5Builder
28+
#include "HEXBuilder.h"
29+
30+
class MD5Builder : HEXBuilder
2931
{
3032
private:
3133
md5_context_t _ctx;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#include <HEXBuilder.h>
2+
3+
void setup() {
4+
Serial.begin(115200);
5+
delay(100);
6+
Serial.println("\n\n\nStart.");
7+
8+
// Convert a HEX string like 6c6c6f20576f726c64 to a binary buffer
9+
//
10+
{
11+
const char * out = "Hello World";
12+
const char * hexin = "48656c6c6f20576f726c6400"; // As the string above is \0 terminated too
13+
14+
unsigned char buff[256];
15+
size_t len = HEXBuilder::hex2bytes(buff, sizeof(buff), hexin);
16+
17+
if (len != 1 + strlen(out))
18+
Serial.println("Odd - length 1 is wrong");
19+
20+
if (memcmp(buff, out, len) != 0)
21+
Serial.println("Odd - decode 1 went wrong");
22+
23+
// Safe to print this binary buffer -- as we've included a \0 in the hex sequence.
24+
//
25+
Serial.printf("IN: <%s>\nOUT <%s\\0>\n", hexin, buff);
26+
};
27+
28+
{
29+
String helloHEX = "48656c6c6f20576f726c64";
30+
const char hello[] = "Hello World";
31+
32+
unsigned char buff[256];
33+
size_t len = HEXBuilder::hex2bytes(buff, sizeof(buff), helloHEX);
34+
35+
if (len != strlen(hello))
36+
Serial.println("Odd - length 2 is wrong");
37+
38+
if (strcmp((char *) buff, hello) != 0)
39+
Serial.println("Odd - decode 2 went wrong");
40+
}
41+
42+
{
43+
const unsigned char helloBytes[] = { 0x48, 0x56, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64 };
44+
String helloHEX = "48566c6c6f20576f726c64";
45+
46+
47+
String out = HEXBuilder::bytes2hex(helloBytes, sizeof(helloBytes));
48+
if (out.length() != 2 * sizeof(helloBytes))
49+
Serial.println("Odd - length 3 is wrong");
50+
51+
// we need to ignore case - as a hex string can be spelled in uppercase and lowercase
52+
//
53+
if (!out.equalsIgnoreCase(helloHEX)) {
54+
Serial.println("Odd - decode 3 went wrong");
55+
}
56+
}
57+
58+
{
59+
const unsigned char helloBytes[] = { 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64 };
60+
const char helloHex[] = "6c6c6f20576f726c64";
61+
62+
char buff[256];
63+
size_t len = HEXBuilder::bytes2hex(buff, sizeof(buff), helloBytes, sizeof(helloBytes));
64+
if (len != 1 + 2 * sizeof(helloBytes))
65+
Serial.println("Odd - length 4 is wrong");
66+
67+
// we need to ignore case - as a hex string can be spelled in uppercase and lowercase
68+
//
69+
if (strcasecmp(buff, helloHex))
70+
Serial.println("Odd - decode 4 went wrong");
71+
}
72+
Serial.println("Done.");
73+
}
74+
75+
void loop() {
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#include <MD5Builder.h>
2+
3+
// Occasionally it is useful to compare a password that the user
4+
// has entered to a build in string. However this means that the
5+
// password ends up `in the clear' in the firmware and in your
6+
// source code.
7+
//
8+
// MD5Builder helps you obfuscate this (it is not terribly secure, MD5
9+
// has been phased out as insecure eons ago) by letting you create an
10+
// MD5 of the data the user entered; and then compare this to an MD5
11+
// string that you have put in your code.
12+
//
13+
void setup() {
14+
Serial.begin(115200);
15+
delay(100);
16+
Serial.println("\n\n\nStart.");
17+
18+
// Check if a password obfuscated in an MD5 actually
19+
// matches the original string.
20+
//
21+
// echo -n "Hello World" | openssl md5
22+
//
23+
{
24+
String md5 = "b10a8db164e0754105b7a99be72e3fe5";
25+
String password = "Hello World";
26+
27+
MD5Builder md;
28+
29+
md.begin();
30+
md.add(password);
31+
md.calculate();
32+
33+
String result = md.toString();
34+
35+
if (!md5.equalsIgnoreCase(result))
36+
Serial.println("Odd - failing MD5 on String");
37+
else
38+
Serial.println("OK!");
39+
}
40+
// Check that this also work if we add the password not as
41+
// a normal string - but as a string with the HEX values.
42+
{
43+
String passwordAsHex = "48656c6c6f20576f726c64";
44+
String md5 = "b10a8db164e0754105b7a99be72e3fe5";
45+
46+
MD5Builder md;
47+
48+
md.begin();
49+
md.addHexString(passwordAsHex);
50+
md.calculate();
51+
52+
String result = md.toString();
53+
54+
if (!md5.equalsIgnoreCase(result)) {
55+
Serial.println("Odd - failing MD5 on hex string");
56+
Serial.println(md5);
57+
Serial.println(result);
58+
}
59+
else
60+
Serial.println("OK!");
61+
62+
}
63+
// Check that this also work if we add the password as
64+
// an unsigned byte array.
65+
{
66+
uint8_t password[] = { 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64 };
67+
String md5 = "b10a8db164e0754105b7a99be72e3fe5";
68+
MD5Builder md;
69+
70+
md.begin();
71+
md.add(password, sizeof(password));
72+
md.calculate();
73+
74+
String result = md.toString();
75+
76+
if (!md5.equalsIgnoreCase(result))
77+
Serial.println("Odd - failing MD5 on byte array");
78+
else
79+
Serial.println("OK!");
80+
81+
// And also check that we can compare this as pure, raw, bytes
82+
//
83+
uint8_t raw[16] = { 0xb1, 0x0a, 0x8d, 0xb1, 0x64, 0xe0, 0x75, 0x41,
84+
0x05, 0xb7, 0xa9, 0x9b, 0xe7, 0x2e, 0x3f, 0xe5
85+
};
86+
uint8_t res[16];
87+
md.getBytes(res);
88+
if (memcmp(raw, res, 16))
89+
Serial.println("Odd - failing MD5 on byte array when compared as bytes");
90+
else
91+
Serial.println("OK!");
92+
93+
}
94+
}
95+
96+
void loop() {
97+
}

0 commit comments

Comments
 (0)