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