github.com/iDigitalFlame/xmt@v0.5.4/tools/crypt_builder.py (about) 1 #!/usr/bin/python3 2 # Copyright (C) 2020 - 2023 iDigitalFlame 3 # 4 # This program is free software: you can redistribute it and/or modify 5 # it under the terms of the GNU General Public License as published by 6 # the Free Software Foundation, either version 3 of the License, or 7 # any later version. 8 # 9 # This program is distributed in the hope that it will be useful, 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 # GNU General Public License for more details. 13 # 14 # You should have received a copy of the GNU General Public License 15 # along with this program. If not, see <https://www.gnu.org/licenses/>. 16 # 17 18 # crypt_builder.py 19 # Generates a crypt mapping string or build based off the build tags supplied. 20 21 from io import BytesIO 22 from json import loads 23 from os import environ 24 from subprocess import run 25 from secrets import token_bytes 26 from sys import stderr, exit, argv 27 from base64 import urlsafe_b64encode 28 from platform import system, architecture 29 30 31 def xor(key, data): 32 if len(data) == 0 or len(key) == 0: 33 return bytearray() 34 r = bytearray(len(data)) 35 for i in range(0, len(r)): 36 r[i] = data[i] ^ key[i % len(key)] 37 return r 38 39 40 def check_tags(args): 41 for x in range(0, len(args)): 42 if args[x] == "-tags": 43 if "crypt" not in [k.strip() for k in args[x + 1].split(",")]: 44 args[x + 1] = f"crypt,{args[x+1].strip()}" 45 return args 46 return args + ["-tags", "crypt"] 47 48 49 def get_env_tags(args): 50 if "GOOS" in environ: 51 o = environ["GOOS"] 52 else: 53 o = system().lower() 54 if "GOARCH" in environ: 55 a = environ["GOARCH"] 56 else: 57 v = architecture() 58 if v[0] == "64bit": 59 a = "amd64" 60 elif v[0] == "32bit": 61 a = "386" 62 else: 63 a = v[0].lower() 64 del v 65 t = [o, a] 66 del o, a 67 for x in range(0, len(args)): 68 if args[x] == "-tags": 69 for e in args[x + 1].split(","): 70 v = e.strip() 71 if v not in t: 72 t.append(v) 73 del v 74 break 75 return t 76 77 78 def use_tag(current, tags): 79 if not isinstance(tags, list) or not isinstance(current, list) or len(current) == 0: 80 return True 81 n = True 82 for t in tags: 83 if t[0] != "!": 84 n = False 85 break 86 # Check negatives first 87 for t in tags: 88 if t[0] != "!": 89 continue 90 if t[1:] in current: 91 return False 92 # Check positives 93 for t in tags: 94 if t[0] == "!": 95 continue 96 if t in current: 97 return True 98 return n 99 100 101 class CryptWriter(BytesIO): 102 __slots__ = ("key",) 103 104 def __init__(self, key=None): 105 BytesIO.__init__(self) 106 if isinstance(key, str) and len(key) > 0: 107 self.key = key.encode("UTF-8") 108 elif isinstance(key, (bytes, bytearray)): 109 self.key = key 110 else: 111 self.key = token_bytes(64) 112 113 def add(self, v): 114 self.write(v.encode("UTF-8")) 115 self.write(bytearray(1)) 116 117 def output(self): 118 return urlsafe_b64encode(xor(self.key, self.getvalue())).decode("UTF-8") 119 120 def key_output(self): 121 return urlsafe_b64encode(self.key).decode("UTF-8") 122 123 def from_file(self, f, tags): 124 with open(f, "r") as b: 125 d = loads(b.read()) 126 if not isinstance(d, dict) or len(d) == 0: 127 return 128 c = [None] * len(d) 129 for k, v in d.items(): 130 if not isinstance(v, dict) or "value" not in v: 131 c[int(k)] = "" 132 continue 133 if not use_tag(tags, v.get("tags")): 134 c[int(k)] = "" 135 else: 136 c[int(k)] = v["value"] 137 print("Including strings in build...") 138 for x in range(0, len(c)): 139 self.add(c[x]) 140 if len(c[x]) == 0: 141 continue 142 s = c[x].replace("\n", "\\n") 143 print(f'+ {x:3} == "{s}"') 144 del s 145 146 147 if __name__ == "__main__": 148 if len(argv) < 3: 149 print(f"{argv[0]} [-no-build] <file> [go build args]", file=stderr) 150 exit(2) 151 152 n = False 153 if argv[1] == "-no-build": 154 if len(argv) < 4: 155 print(f"{argv[0]} [-no-build] <file> [go build args]", file=stderr) 156 exit(2) 157 n = True 158 argv.remove("-no-build") 159 160 w = CryptWriter() 161 w.from_file(argv[1], get_env_tags(argv[2:])) 162 163 if n: 164 print("Add this to ldflags:\n") 165 print( 166 f"-X 'github.com/iDigitalFlame/xmt/util/crypt.key={w.key_output()}'", end="" 167 ) 168 print(f" -X 'github.com/iDigitalFlame/xmt/util/crypt.payload={w.output()}'") 169 exit(0) 170 171 a = [ 172 "go", 173 "build", 174 "-ldflags", 175 f"-w -s -X " 176 f"'github.com/iDigitalFlame/xmt/util/crypt.key={w.key_output()}'" 177 f" -X 'github.com/iDigitalFlame/xmt/util/crypt.payload={w.output()}'", 178 ] 179 a.extend(check_tags(argv[2:])) 180 run(a, env=environ, check=True, text=True, capture_output=True) 181 print() 182 del a