1 #include <ccan/short_types/short_types.h>
2 #include <common/descriptor_checksum.h>
3 #include <stdlib.h>
4 
5 
6 static const char CHECKSUM_CHARSET[] = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
7 
8 static const char INPUT_CHARSET[] =
9 	"0123456789()[],'/*abcdefgh@:$%{}"
10 	"IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~"
11 	"ijklmnopqrstuvwxyzABCDEFGH`#\"\\ ";
12 
charset_find(char ch)13 static inline int charset_find(char ch) {
14 	for (size_t i = 0; i < sizeof(INPUT_CHARSET); i++) {
15 		if (INPUT_CHARSET[i] == ch)
16 			return i;
17 	}
18 	return -1;
19 }
20 
polymod(u64 c,int val)21 static u64 polymod(u64 c, int val)
22 {
23 	u8 c0 = c >> 35;
24 	c = ((c & 0x7ffffffff) << 5) ^ val;
25 	if (c0 & 1) c ^= 0xf5dee51989;
26 	if (c0 & 2) c ^= 0xa9fdca3312;
27 	if (c0 & 4) c ^= 0x1bab10e32d;
28 	if (c0 & 8) c ^= 0x3706b1677a;
29 	if (c0 & 16) c ^= 0x644d626ffd;
30 	return c;
31 }
32 
33 
descriptor_checksum(const char * descriptor,int desc_size,struct descriptor_checksum * checksum)34 bool descriptor_checksum(const char *descriptor, int desc_size,
35 			 struct descriptor_checksum *checksum)
36 {
37 	checksum->csum[0] = 0;
38 
39 	int j;
40 	u64 c = 1;
41 	int cls = 0;
42 	int clscount = 0;
43 
44 	for (int i = 0; i < desc_size; i++) {
45 		char ch = descriptor[i];
46 		int pos = charset_find(ch);
47 		if (pos == -1) {
48 			checksum->csum[0] = 0;
49 			return false;
50 		}
51 		/* Emit a symbol for the position inside the group, for every
52 		 * character. */
53 		c = polymod(c, pos & 31);
54 
55 		/* Accumulate the group numbers */
56 		cls = cls * 3 + (pos >> 5);
57 
58 		if (++clscount == 3) {
59 			c = polymod(c, cls);
60 			cls = 0;
61 			clscount = 0;
62 		}
63 	}
64 
65 	if (clscount > 0)
66 		c = polymod(c, cls);
67 
68 	/* Shift further to determine the checksum. */
69 	for (j = 0; j < DESCRIPTOR_CHECKSUM_LENGTH; ++j)
70 		c = polymod(c, 0);
71 
72 	/* Prevent appending zeroes from not affecting the checksum. */
73 	c ^= 1;
74 
75 	for (j = 0; j < DESCRIPTOR_CHECKSUM_LENGTH; ++j)
76 		checksum->csum[j] = CHECKSUM_CHARSET[(c >> (5 * (7 - j))) & 31];
77 
78 	checksum->csum[DESCRIPTOR_CHECKSUM_LENGTH] = 0;
79 
80 	return true;
81 }
82