golang.zx2c4.com/wireguard/windows@v0.5.4-0.20230123132234-dcc0eb72a04b/installer/fetcher/filelist.c (about) 1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2020-2022 Jason A. Donenfeld. All Rights Reserved. 4 */ 5 6 #include "constants.h" 7 #include "crypto.h" 8 #include "filelist.h" 9 #include <stdbool.h> 10 #include <string.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 14 static inline int decode_base64(const char src[static 4]) 15 { 16 int val = 0; 17 18 for (unsigned int i = 0; i < 4; ++i) 19 val |= (-1 20 + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64)) 21 + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70)) 22 + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5)) 23 + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63) 24 + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64) 25 ) << (18 - 6 * i); 26 return val; 27 } 28 29 bool signify_pubkey_from_base64(uint8_t key[static 42], const char base64[static 56]) 30 { 31 unsigned int i; 32 volatile uint8_t ret = 0; 33 int val; 34 35 for (i = 0; i < 42 / 3; ++i) { 36 val = decode_base64(&base64[i * 4]); 37 ret |= (uint32_t)val >> 31; 38 key[i * 3 + 0] = (val >> 16) & 0xff; 39 key[i * 3 + 1] = (val >> 8) & 0xff; 40 key[i * 3 + 2] = val & 0xff; 41 } 42 43 return 1 & ((ret - 1) >> 8); 44 } 45 46 bool signify_signature_from_base64(uint8_t sig[static 74], const char base64[static 100]) 47 { 48 unsigned int i; 49 volatile uint8_t ret = 0; 50 int val; 51 52 if (base64[99] != '=') 53 return false; 54 55 for (i = 0; i < 74 / 3; ++i) { 56 val = decode_base64(&base64[i * 4]); 57 ret |= (uint32_t)val >> 31; 58 sig[i * 3 + 0] = (val >> 16) & 0xff; 59 sig[i * 3 + 1] = (val >> 8) & 0xff; 60 sig[i * 3 + 2] = val & 0xff; 61 } 62 val = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' }); 63 ret |= ((uint32_t)val >> 31) | (val & 0xff); 64 sig[i * 3 + 0] = (val >> 16) & 0xff; 65 sig[i * 3 + 1] = (val >> 8) & 0xff; 66 67 return 1 & ((ret - 1) >> 8); 68 } 69 70 bool hash_from_hex(uint8_t hash[static 32], const char hex[static 64]) 71 { 72 uint8_t c, c_acc, c_alpha0, c_alpha, c_num0, c_num, c_val; 73 volatile uint8_t ret = 0; 74 75 for (unsigned int i = 0; i < 64; i += 2) { 76 c = (uint8_t)hex[i]; 77 c_num = c ^ 48U; 78 c_num0 = (c_num - 10U) >> 8; 79 c_alpha = (c & ~32U) - 55U; 80 c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8; 81 ret |= ((c_num0 | c_alpha0) - 1) >> 8; 82 c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha); 83 c_acc = c_val * 16U; 84 85 c = (uint8_t)hex[i + 1]; 86 c_num = c ^ 48U; 87 c_num0 = (c_num - 10U) >> 8; 88 c_alpha = (c & ~32U) - 55U; 89 c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8; 90 ret |= ((c_num0 | c_alpha0) - 1) >> 8; 91 c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha); 92 hash[i / 2] = c_acc | c_val; 93 } 94 95 return 1 & ((ret - 1) >> 8); 96 } 97 98 static uint64_t parse_version(const char *str, size_t len) 99 { 100 uint64_t version = 0; 101 unsigned long nibble; 102 const char *limit = str + len; 103 char *end; 104 105 for (int shift = 64 - 16; shift >= 0; shift -= 16, str = end + 1) { 106 if (str[0] == '-' || str[0] == '+') 107 return 0; 108 nibble = strtoul(str, &end, 10); 109 if (nibble > UINT16_MAX) 110 return 0; 111 version |= (uint64_t)nibble << shift; 112 if (end >= limit) 113 break; 114 if (end[0] != '.') 115 return 0; 116 } 117 return version; 118 } 119 120 bool extract_newest_file(char filename[static MAX_FILENAME_LEN], uint8_t hash[static 32], const char *list, size_t len, const char *arch) 121 { 122 const char *first_nl, *second_nl, *line_start, *line_end; 123 char msi_prefix[sizeof(msi_arch_prefix) + 10]; 124 int msi_prefix_len; 125 uint8_t pubkey[42], signature[74]; 126 uint64_t biggest_version = 0, version; 127 128 if ((msi_prefix_len = _snprintf_s(msi_prefix, sizeof(msi_prefix), _TRUNCATE, msi_arch_prefix, arch)) < 0) 129 return false; 130 if (!signify_pubkey_from_base64(pubkey, release_public_key_base64)) 131 return false; 132 first_nl = memchr(list, '\n', len); 133 if (!first_nl) 134 return false; 135 second_nl = memchr(first_nl + 1, '\n', len - (first_nl + 1 - list)); 136 if (!second_nl) 137 return false; 138 if (len < 19 || memcmp(list, "untrusted comment: ", 19)) 139 return false; 140 if (second_nl - first_nl != 101) 141 return false; 142 if (!signify_signature_from_base64(signature, first_nl + 1)) 143 return false; 144 if (memcmp(pubkey, signature, 10)) 145 return false; 146 if (!ed25519_verify(signature + 10, pubkey + 10, second_nl + 1, len - (second_nl + 1 - list))) 147 return false; 148 for (line_start = second_nl + 1; line_start < list + len; line_start = line_end + 1) { 149 line_end = memchr(line_start + 1, '\n', len - (line_start + 1 - list)); 150 if (!line_end) 151 break; 152 if ((size_t)(line_end - line_start) < (64 + 2 + msi_prefix_len + strlen(msi_suffix) + 1) || line_start[64] != ' ' || line_start[65] != ' ') 153 continue; 154 if (memcmp(msi_prefix, line_start + 66, msi_prefix_len) || memcmp(msi_suffix, line_end - strlen(msi_suffix), strlen(msi_suffix))) 155 continue; 156 if (line_end - line_start - 66 > MAX_FILENAME_LEN - 1) 157 continue; 158 version = parse_version(line_start + 66 + msi_prefix_len, line_end - strlen(msi_suffix) - line_start - 66 - msi_prefix_len); 159 if (version < biggest_version) 160 continue; 161 if (!hash_from_hex(hash, line_start)) 162 continue; 163 memcpy(filename, line_start + 66, line_end - line_start - 66); 164 filename[line_end - line_start - 66] = '\0'; 165 biggest_version = version; 166 } 167 return biggest_version > 0; 168 }