mirror of
https://github.com/netinvent/npbackup.git
synced 2024-09-20 06:46:13 +08:00
Improve windows signature script to allow EV automatic signing
This commit is contained in:
parent
cbade449b2
commit
bb6a68798f
|
@ -262,10 +262,13 @@ The file `customization.py` also contains OEM strings that can be safely changed
|
|||
|
||||
In order to fully protect the AES key that is needed to support NPBackup, one can compile the program with Nuitka.
|
||||
Compiling needs restic binary for the target platform in `RESTIC_SOURCE_FILES` folder, files must be named `restic_{version}_{platform}_{arch}[.extension]` like provided by restic.net or [github](https://github.com/restic/restic)
|
||||
Linux binaries need to be made executable in the `RESTIC_SOURCE_FILES` folder.
|
||||
Linux binaries need to be made executable in the `RESTIC_SOURCE_FILES` folder.
|
||||
You can use `update_restic.sh` script in `RESTIC_SOURCE_FILES` to download binaries.
|
||||
|
||||
You'll need to change the default AES key in `secrets.py`, see the documentation in the file itself.
|
||||
|
||||
One you're setup, you need to install compilation tools, see `requirements-compilation.txt` file.
|
||||
|
||||
Compile options are available in `compile.py`.
|
||||
|
||||
We maintain a special 32 bit binary for Windows 7 which allows to backup those old machines until they get replaced.
|
||||
|
@ -282,7 +285,7 @@ Official binaries for Windows provided by NetInvent are signed with a certificat
|
|||
Also, official binaries are compiled using Nuitka Commercial grade, which is more secure in storing secrets.
|
||||
|
||||
Pre-compiled builds for Windows have been code signed with NetInvent's EV certificate, using [windows_tools.signtool](github.com/netinvent/windows_tools)
|
||||
Signing on a Windows machine with Windows SDK installed can be done via `bin\sign_windows.py` script.
|
||||
Signing on a Windows machine with Windows SDK installed can be done via `windows\sign_windows.py` script.
|
||||
Alternatively, you can sign executables via:
|
||||
```
|
||||
from windows_tools.signtool import SignTool
|
||||
|
|
|
@ -7,8 +7,8 @@ __intname__ = "npbackup.compile"
|
|||
__author__ = "Orsiris de Jong"
|
||||
__copyright__ = "Copyright (C) 2023-2024 NetInvent"
|
||||
__license__ = "GPL-3.0-only"
|
||||
__build__ = "2024090301"
|
||||
__version__ = "2.0.1"
|
||||
__build__ = "2024090801"
|
||||
__version__ = "2.1.0"
|
||||
|
||||
|
||||
"""
|
||||
|
@ -18,6 +18,9 @@ Nuitka compilation script tested for
|
|||
- Linux i386
|
||||
- Linux i686
|
||||
- Linux armv71
|
||||
|
||||
Also optionally signs windows executables
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
@ -28,6 +31,8 @@ import argparse
|
|||
import atexit
|
||||
from command_runner import command_runner
|
||||
from ofunctions.platform import python_arch, get_os
|
||||
if os.name == "nt":
|
||||
from npbackup.windows.sign_windows import sign
|
||||
|
||||
AUDIENCES = ["public", "private"]
|
||||
BUILD_TYPES = ["cli", "gui", "viewer"]
|
||||
|
@ -177,7 +182,7 @@ def have_nuitka_commercial():
|
|||
return False
|
||||
|
||||
|
||||
def compile(arch: str, audience: str, build_type: str, onefile: bool, create_tar_only: bool):
|
||||
def compile(arch: str, audience: str, build_type: str, onefile: bool, create_tar_only: bool, ev_cert_data: str = None):
|
||||
if build_type not in BUILD_TYPES:
|
||||
print("CANNOT BUILD BOGUS BUILD TYPE")
|
||||
sys.exit(1)
|
||||
|
@ -327,6 +332,9 @@ def compile(arch: str, audience: str, build_type: str, onefile: bool, create_tar
|
|||
fh.write(npbackup_version)
|
||||
print(f"COMPILED {'WITH SUCCESS' if not errors else 'WITH ERRORS'}")
|
||||
|
||||
if os.name == "nt" and ev_cert_data:
|
||||
sign(ev_cert_data=ev_cert_data, dry_run=args.dry_run)
|
||||
|
||||
if not onefile:
|
||||
if not create_archive(
|
||||
platform=platform,
|
||||
|
@ -417,6 +425,15 @@ if __name__ == "__main__":
|
|||
help="Build single file executable (more prone to AV detection)",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--sign",
|
||||
type=str,
|
||||
dest="ev_cert_data",
|
||||
default=False,
|
||||
required=False,
|
||||
help="Digitally sign windows executables",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--create-tar-only",
|
||||
action="store_true",
|
||||
|
@ -472,7 +489,8 @@ if __name__ == "__main__":
|
|||
audience=audience,
|
||||
build_type=build_type,
|
||||
onefile=args.onefile,
|
||||
create_tar_only=create_tar_only
|
||||
create_tar_only=create_tar_only,
|
||||
ev_cert_data=args.ev_cert_data
|
||||
)
|
||||
if not create_tar_only:
|
||||
audience_build = "private" if private_build else "public"
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
#! /usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of npbackup
|
||||
|
||||
__intname__ = "npbackup.sign_windows"
|
||||
__author__ = "Orsiris de Jong"
|
||||
__copyright__ = "Copyright (C) 2023-2024 NetInvent"
|
||||
__license__ = "GPL-3.0-only"
|
||||
__build__ = "2024060401"
|
||||
__version__ = "1.1.2"
|
||||
|
||||
|
||||
import os
|
||||
|
||||
try:
|
||||
from windows_tools.signtool import SignTool
|
||||
except ImportError:
|
||||
print("This tool needs windows_tools.signtool >= 0.3.1")
|
||||
|
||||
|
||||
basepath = r"C:\GIT\npbackup\BUILDS"
|
||||
audiences = ["private", "public"]
|
||||
arches = ["x86", "x64"]
|
||||
binaries = ["npbackup-cli", "npbackup-gui", "npbackup-viewer"]
|
||||
|
||||
signer = SignTool()
|
||||
|
||||
for audience in audiences:
|
||||
for arch in arches:
|
||||
for binary in binaries:
|
||||
one_file_exe_path = exe_path = os.path.join(
|
||||
basepath, audience, "windows", arch, binary + f"-{arch}.exe"
|
||||
)
|
||||
standalone_exe_path = os.path.join(
|
||||
basepath, audience, "windows", arch, binary + ".dist", binary + f".exe"
|
||||
)
|
||||
for exe_file in (one_file_exe_path, standalone_exe_path):
|
||||
if os.path.isfile(exe_file):
|
||||
print(f"Signing {exe_file}")
|
||||
result = signer.sign(exe_file, bitness=arch)
|
||||
if not result:
|
||||
raise EnvironmentError(
|
||||
"Could not sign executable ! Is the PKI key connected ?"
|
||||
)
|
2
npbackup/requirements-compilation.txt
Normal file
2
npbackup/requirements-compilation.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
nuitka>=2.4.8
|
||||
windows_tools.signtool>0.5.0
|
132
npbackup/windows/sign_windows.py
Normal file
132
npbackup/windows/sign_windows.py
Normal file
|
@ -0,0 +1,132 @@
|
|||
#! /usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of npbackup
|
||||
|
||||
__intname__ = "npbackup.sign_windows"
|
||||
__author__ = "Orsiris de Jong"
|
||||
__copyright__ = "Copyright (C) 2023-2024 NetInvent"
|
||||
__license__ = "GPL-3.0-only"
|
||||
__build__ = "2024090801"
|
||||
__version__ = "1.2.0"
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
from cryptidy.symmetric_encryption import decrypt_message
|
||||
|
||||
try:
|
||||
from windows_tools.signtool import SignTool
|
||||
except ImportError:
|
||||
print("This tool needs windows_tools.signtool >= 0.4.0")
|
||||
|
||||
|
||||
basepath = r"C:\GIT\npbackup\BUILDS"
|
||||
audiences = ["private", "public"]
|
||||
arches = ["x86", "x64"]
|
||||
binaries = ["npbackup-cli", "npbackup-gui", "npbackup-viewer"]
|
||||
|
||||
|
||||
def check_private_ev():
|
||||
"""
|
||||
Test if we have private ev data
|
||||
"""
|
||||
try:
|
||||
from PRIVATE._private_ev_data import AES_EV_KEY
|
||||
from PRIVATE._private_obfuscation import obfuscation
|
||||
|
||||
print("We have private EV certifcate DATA")
|
||||
return obfuscation(AES_EV_KEY)
|
||||
except ImportError as exc:
|
||||
print("ERROR: Cannot load private EV certificate DATA: {}".format(exc))
|
||||
sys.exit()
|
||||
|
||||
|
||||
def get_ev_data(cert_data_path):
|
||||
"""
|
||||
This retrieves specific data for crypto env
|
||||
"""
|
||||
aes_key = check_private_ev()
|
||||
with open(cert_data_path, "rb") as fp:
|
||||
ev_cert_data = fp.read()
|
||||
try:
|
||||
timestamp, ev_cert = decrypt_message(ev_cert_data, aes_key=aes_key)
|
||||
(
|
||||
pkcs12_certificate,
|
||||
pkcs12_password,
|
||||
container_name,
|
||||
cryptographic_provider,
|
||||
) = ev_cert
|
||||
except Exception as exc:
|
||||
print("EV Cert data is corrupt")
|
||||
sys.exit(1)
|
||||
return pkcs12_certificate, pkcs12_password, container_name, cryptographic_provider
|
||||
|
||||
|
||||
def sign(ev_cert_data: str = None, dry_run: bool = False):
|
||||
if ev_cert_data:
|
||||
(
|
||||
pkcs12_certificate,
|
||||
pkcs12_password,
|
||||
container_name,
|
||||
cryptographic_provider,
|
||||
) = get_ev_data(ev_cert_data)
|
||||
signer = SignTool(
|
||||
certificate=pkcs12_certificate,
|
||||
pkcs12_password=pkcs12_password,
|
||||
container_name=container_name,
|
||||
cryptographic_provider=cryptographic_provider,
|
||||
)
|
||||
else:
|
||||
signer = SignTool()
|
||||
|
||||
for audience in audiences:
|
||||
for arch in arches:
|
||||
for binary in binaries:
|
||||
one_file_exe_path = os.path.join(
|
||||
basepath, audience, "windows", arch, binary + f"-{arch}.exe"
|
||||
)
|
||||
standalone_exe_path = os.path.join(
|
||||
basepath,
|
||||
audience,
|
||||
"windows",
|
||||
arch,
|
||||
binary + ".dist",
|
||||
binary + f".exe",
|
||||
)
|
||||
for exe_file in (one_file_exe_path, standalone_exe_path):
|
||||
if os.path.isfile(exe_file):
|
||||
print(f"Signing {exe_file}")
|
||||
result = signer.sign(exe_file, bitness=arch, dry_run=dry_run)
|
||||
if not result:
|
||||
# IMPORTANT: If using an automated crypto USB EV token, we need to stop on error so we don't lock ourselves out of the token with bad password attempts
|
||||
raise EnvironmentError(
|
||||
"Could not sign executable ! Is the PKI key connected ?"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="npbackup sign_windows.py",
|
||||
description="Windows executable signer for NPBackup",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
default=False,
|
||||
required=False,
|
||||
help="Don't actually sign anything, just test command",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ev-cert-data",
|
||||
type=str,
|
||||
default=None,
|
||||
required=False,
|
||||
help="Path to EV certificate data",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
sign(ev_cert_data=args.ev_cert_data, dry_run=args.dry_run)
|
Loading…
Reference in a new issue