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
|
/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
It is considered beerware. Prost. Skol. Cheers or whatever.
$id$ */
/* System */
#include <stdint.h>
#include <string.h>
/* Opentracker */
#include "ot_hex.h"
#include "test.h"
static const uint8_t sample[20] = {0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xff, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0};
static void case_encode(void) {
char dest[64];
test_case("a hash encodes to forty upper case digits and a terminator");
memset(dest, 0x7f, sizeof(dest));
CHECK_PTR(hex_fromhash(dest, sample), dest);
CHECK_STR(dest, "000123456789ABCDEFFF10203040506070809" "0A0");
CHECK_INT(strlen(dest), 40);
CHECK_INT((unsigned char)dest[41], 0x7f);
}
static void case_decode(void) {
ot_hash hash;
test_case("forty digits decode back to the hash");
CHECK(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809" "0A0"));
CHECK_MEM(hash, sample, sizeof(sample));
}
static void case_round_trip(void) {
ot_hash hash;
char text[64];
size_t i;
test_case("encode and decode are inverses over every byte value");
for (i = 0; i < 256; ++i) {
uint8_t probe[20];
memset(probe, (uint8_t)i, sizeof(probe));
hex_fromhash(text, probe);
if (!hex_tohash(hash, text) || memcmp(hash, probe, sizeof(probe))) {
test_failf(__FILE__, __LINE__, "round trip failed for byte value %zu", i);
return;
}
}
++test_checks;
}
static void case_decode_is_case_insensitive(void) {
ot_hash upper, lower;
test_case("lower case digits decode the same as upper case");
CHECK(hex_tohash(upper, "ABCDEF0123456789ABCDEF0123456789ABCDEF01"));
CHECK(hex_tohash(lower, "abcdef0123456789abcdef0123456789abcdef01"));
CHECK_MEM(lower, upper, sizeof(ot_hash));
}
static void case_rejects_non_digits(void) {
ot_hash hash;
test_case("a non hex character anywhere rejects the whole string");
CHECK_INT(hex_tohash(hash, "g00123456789ABCDEFFF102030405060708090A0"), 0);
CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090Ag"), 0);
CHECK_INT(hex_tohash(hash, "000123456789ABCDEF:F102030405060708090A"), 0);
}
static void case_rejects_short_input(void) {
ot_hash hash;
test_case("a string shorter than forty digits is rejected");
CHECK_INT(hex_tohash(hash, ""), 0);
CHECK_INT(hex_tohash(hash, "00"), 0);
CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090A"), 0);
}
static void case_leaves_hash_untouched_on_failure(void) {
ot_hash hash;
test_case("a rejected string leaves the destination untouched");
memset(hash, 0x5a, sizeof(hash));
CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809XX0"), 0);
CHECK_MEM(hash, "\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a", sizeof(hash));
}
static void case_stops_at_the_terminator(void) {
/* The saved state parser hands this whole buffers that may end early. The
decoder must not read past the zero that stopped it. */
char guarded[8];
ot_hash hash;
test_case("decoding stops at a terminator rather than running past it");
memcpy(guarded, "0012", 4);
guarded[4] = 0;
memset(guarded + 5, 'f', 3);
CHECK_INT(hex_tohash(hash, guarded), 0);
}
static void case_isdigit(void) {
test_case("hex_isdigit accepts exactly the hex digits");
CHECK(hex_isdigit('0'));
CHECK(hex_isdigit('9'));
CHECK(hex_isdigit('a'));
CHECK(hex_isdigit('f'));
CHECK(hex_isdigit('A'));
CHECK(hex_isdigit('F'));
CHECK_INT(hex_isdigit('g'), 0);
CHECK_INT(hex_isdigit('G'), 0);
CHECK_INT(hex_isdigit(':'), 0);
CHECK_INT(hex_isdigit('\n'), 0);
CHECK_INT(hex_isdigit(0), 0);
CHECK_INT(hex_isdigit(0x80), 0);
}
void test_hex(void) {
case_encode();
case_decode();
case_round_trip();
case_decode_is_case_insensitive();
case_rejects_non_digits();
case_rejects_short_input();
case_leaves_hash_untouched_on_failure();
case_stops_at_the_terminator();
case_isdigit();
}
|