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  }