Last active
January 23, 2022 20:56
-
-
Save al3xtjames/d61529c1fa30268a4a6c3024de61ea57 to your computer and use it in GitHub Desktop.
Miscellaneous Secure Boot scripts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
import argparse | |
import datetime | |
import pathlib | |
import shutil | |
import ssl | |
import struct | |
import subprocess | |
import sys | |
import typing | |
import uuid | |
EFI_CERT_TYPE_PKCS7_GUID = uuid.UUID("4aafd29d-68df-49ee-8aa9-347d375665a7") | |
EFI_CERT_X509_GUID = uuid.UUID("a5c059a1-94e4-4aa7-87b5-ab155c2bf072") | |
EFI_GLOBAL_VARIABLE_GUID = uuid.UUID("8be4df61-93ca-11d2-aa0d-00e098032b8c") | |
EFI_IMAGE_SECURITY_DATABASE_GUID = uuid.UUID("d719b2cb-3d3a-4596-a3bc-dad00e67656f") | |
EFI_VARIABLE_NON_VOLATILE = 1 | |
EFI_VARIABLE_BOOTSERVICE_ACCESS = 2 | |
EFI_VARIABLE_RUNTIME_ACCESS = 4 | |
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS = 32 | |
WIN_CERT_TYPE_EFI_GUID = 0x0EF1 | |
WIN_CERT_REVISION = 0x0200 | |
# UEFI Specification, version 2.8, §32.4.1: Signature Database | |
def gen_efi_sig_list(cert_file: pathlib.Path, owner_guid: uuid.UUID) -> bytes: | |
with open(cert_file, "rb") as fp: | |
cert = fp.read() | |
if cert.startswith(b"-----BEGIN CERTIFICATE-----"): | |
cert = ssl.PEM_cert_to_DER_cert(cert.decode("ascii")) | |
efi_sig_list = bytearray() | |
# EFI_SIGNATURE_LIST | |
# SignatureType | |
efi_sig_list.extend(EFI_CERT_X509_GUID.bytes_le) | |
# SignatureListSize, SignatureHeaderSize, SignatureSize | |
efi_sig_list.extend(struct.pack("<3I", 16 + 4 + 4 + 4 + 16 + len(cert), 0, 16 + len(cert))) | |
# EFI_SIGNATURE_DATA | |
# SignatureOwner | |
efi_sig_list.extend(owner_guid.bytes_le) | |
# SignatureData | |
efi_sig_list.extend(cert) | |
return efi_sig_list | |
def run(args, *, input=None, check=False, encoding=None) -> subprocess.CompletedProcess: | |
try: | |
return subprocess.run(args, input=input, capture_output=True, check=check, encoding=encoding) | |
except subprocess.CalledProcessError as e: | |
print(e.stderr.decode(), file=sys.stderr) | |
raise e | |
def openssl_gen_pkcs7_sig(data: bytes, key: str, cert_file: pathlib.Path, engine: typing.Union[str, None]) -> bytes: | |
openssl_args = [openssl_path, "smime", "-sign", "-binary", "-noattr", "-md", "sha256", "-outform", "der", | |
"-inkey", key, "-signer", cert_file] | |
if engine is not None: | |
openssl_args += ["-engine", engine, "-keyform", "engine"] | |
proc = run(openssl_args, check=True, input=data) | |
return proc.stdout | |
# UEFI Specification version 2.8, §8.2.2: Using the EFI_VARIABLE_AUTHENTICATION_2 descriptor | |
def sign_efi_sig_list(variable_name: str, efi_sig_list: bytes, auth_var_file: pathlib.Path, key: str, | |
cert_file: pathlib.Path, engine: typing.Union[str, None]): | |
timestamp = datetime.datetime.utcnow() | |
payload = bytearray() | |
# VariableName | |
payload.extend(variable_name.encode("utf_16_le")) | |
# VendorGuid | |
if variable_name in ("PK", "KEK"): | |
payload.extend(EFI_GLOBAL_VARIABLE_GUID.bytes_le) | |
elif variable_name in ("db", "dbx"): | |
payload.extend(EFI_IMAGE_SECURITY_DATABASE_GUID.bytes_le) | |
# Attributes | |
payload.extend(struct.pack("<I", EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | | |
EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) | |
# TimeStamp | |
payload.extend(struct.pack("<H6BIh2B", timestamp.year, timestamp.month, timestamp.day, timestamp.hour, | |
timestamp.minute, timestamp.second, 0, 0, 0, 0, 0)) | |
# VariableContent | |
payload.extend(efi_sig_list) | |
sig = openssl_gen_pkcs7_sig(payload, key, cert_file, engine) | |
efi_var_auth2 = bytearray() | |
# EFI_VARIABLE_AUTHENTICATION_2 | |
# TimeStamp | |
efi_var_auth2.extend(struct.pack("<H6BIh2B", timestamp.year, timestamp.month, timestamp.day, timestamp.hour, | |
timestamp.minute, timestamp.second, 0, 0, 0, 0, 0)) | |
# dwLength, wRevision, wCertificateType | |
efi_var_auth2.extend(struct.pack("<I2H", 4 + 2 + 2 + 16 + len(sig), WIN_CERT_REVISION, WIN_CERT_TYPE_EFI_GUID)) | |
# CertType | |
efi_var_auth2.extend(EFI_CERT_TYPE_PKCS7_GUID.bytes_le) | |
# CertData | |
efi_var_auth2.extend(sig) | |
# VariableContent | |
efi_var_auth2.extend(efi_sig_list) | |
with open(auth_var_file, "wb") as fp: | |
fp.write(efi_var_auth2) | |
def gen_auth_vars(pk: str, pk_cert_file: pathlib.Path, kek: str, kek_cert_file: pathlib.Path, | |
db_cert_files: list[pathlib.Path], dbx_file: typing.Union[str, None], owner_guid: uuid.UUID, | |
output_dir: pathlib.Path, engine: typing.Union[str, None]) -> None: | |
pk_esl = gen_efi_sig_list(pk_cert_file, owner_guid) | |
kek_esl = gen_efi_sig_list(kek_cert_file, owner_guid) | |
db_esl = bytearray() | |
for db_cert_file in db_cert_files: | |
db_esl.extend(gen_efi_sig_list(db_cert_file, owner_guid)) | |
print("Generating PK.auth") | |
sign_efi_sig_list("PK", pk_esl, output_dir / "PK.auth", pk, pk_cert_file, engine) | |
print("Generating KEK.auth") | |
sign_efi_sig_list("KEK", kek_esl, output_dir / "KEK.auth", pk, pk_cert_file, engine) | |
print("Generating db.auth") | |
sign_efi_sig_list("db", db_esl, output_dir / "db.auth", kek, kek_cert_file, engine) | |
if dbx_file is not None: | |
print("Generating dbx.auth") | |
with open(dbx_file, "rb") as fp: | |
dbx = fp.read() | |
# The DBX files from uefi.org are already signed with Microsoft's KEK. | |
# Skip over EFI_VARIABLE_AUTHENTICATION_2 to get the EFI_SIGNATURE_LIST. | |
dbx_esl_start = 16 + struct.unpack_from("<I", dbx, 16)[0] | |
dbx_esl = dbx[dbx_esl_start:] | |
sign_efi_sig_list("dbx", dbx_esl, output_dir / "dbx.auth", kek, kek_cert_file, engine) | |
def check_file(file: pathlib.Path) -> None: | |
if not file.exists(): | |
print(str(file), "does not exist", file=sys.stderr) | |
sys.exit(1) | |
def find_executable(name: str) -> str: | |
path = shutil.which(name) | |
if not path: | |
print(name, "was not found in PATH", file=sys.stderr) | |
sys.exit(1) | |
return path | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description="UEFI authenticated variable generator") | |
parser.add_argument("-e", "--engine", type=str, help="OpenSSL engine for key operations") | |
parser.add_argument("-g", "--owner-guid", type=uuid.UUID, required=True, help="Owner GUID") | |
parser.add_argument("--pk", type=str, required=True, help="Path to platform key (or key URI for engines)") | |
parser.add_argument("--pk-cert", type=pathlib.Path, required=True, help="Path to platform key certificate") | |
parser.add_argument("--kek", type=str, required=True, help="Path to key exchange key (or key URI for engines)") | |
parser.add_argument("--kek-cert", type=pathlib.Path, required=True, help="Path to key exchange key certificate") | |
parser.add_argument("--dbx", type=pathlib.Path, help="Path to signed revocation list file (dbxupdate_x64.bin)") | |
parser.add_argument("-o", "--output-dir", type=pathlib.Path, help="Output directory") | |
parser.add_argument("db-cert", type=pathlib.Path, nargs="+", | |
help="Additional certificates to add to the signature database (db)") | |
args = parser.parse_args() | |
check_file(args.pk_cert) | |
check_file(args.kek_cert) | |
check_file(args.output_dir) | |
if args.dbx is not None: | |
check_file(args.dbx) | |
db_certs = getattr(args, "db-cert") | |
for cert in db_certs: | |
check_file(cert) | |
openssl_path = find_executable("openssl") | |
gen_auth_vars(args.pk, args.pk_cert, args.kek, args.kek_cert, db_certs, args.dbx, args.owner_guid, args.output_dir, | |
args.engine) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse | |
import hashlib | |
import pathlib | |
import plistlib | |
import shutil | |
import subprocess | |
import sys | |
import tempfile | |
import typing | |
def run(args, *, input=None, check=False, encoding=None) -> subprocess.CompletedProcess: | |
try: | |
return subprocess.run(args, input=input, capture_output=True, check=check, encoding=encoding) | |
except subprocess.CalledProcessError as e: | |
print(e.stderr.decode(), file=sys.stderr) | |
raise e | |
def verify_efi_binary(efi_file: pathlib.Path, cert_file: pathlib.Path) -> bool: | |
proc = run([sbverify_path, "--cert", str(cert_file), str(efi_file)]) | |
return proc.returncode == 0 | |
def sign_efi_binary(efi_file: pathlib.Path, key: str, cert_file: pathlib.Path, | |
engine: typing.Union[str, None]) -> None: | |
if verify_efi_binary(efi_file, cert_file): | |
return | |
proc = run([sbverify_path, "--list", str(efi_file)], check=True) | |
num_signatures = 0 | |
for line in proc.stdout.splitlines(): | |
if line.startswith(b"signature "): | |
num_signatures += 1 | |
while num_signatures > 0: | |
run([sbattach_path, "--remove", str(efi_file)], check=True) | |
num_signatures -= 1 | |
sbsign_args = [sbsign_path, "--key", key, "--cert", str(cert_file), "--output", str(efi_file)] | |
if engine is not None: | |
sbsign_args += ["--engine", engine] | |
sbsign_args += [str(efi_file)] | |
run(sbsign_args, check=True) | |
def openssl_verify_signed_digest(file: pathlib.Path, sig_file: pathlib.Path, cert_file: pathlib.Path) -> bool: | |
with tempfile.NamedTemporaryFile() as fp: | |
run([openssl_path, "x509", "-pubkey", "-out", fp.name, "-in", str(cert_file)], check=True) | |
proc = run([openssl_path, "dgst", "-sha256", "-verify", fp.name, "-signature", str(sig_file), str(file)]) | |
return proc.returncode == 0 | |
def openssl_gen_signed_digest(file: pathlib.Path, key: str, cert_file: pathlib.Path, sig_file: pathlib.Path, | |
engine: typing.Union[str, None]) -> None: | |
if sig_file.exists(): | |
if openssl_verify_signed_digest(file, sig_file, cert_file): | |
return | |
else: | |
sig_file.unlink() | |
openssl_args = [openssl_path, "dgst", "-sha256", "-sign", key, "-out", str(sig_file)] | |
if engine is not None: | |
openssl_args += ["-engine", engine, "-keyform", "engine"] | |
openssl_args += [str(file)] | |
run(openssl_args, check=True) | |
def gen_oc_rsa_pub_key(cert_file: pathlib.Path) -> bytes: | |
proc = run([openssl_path, "x509", "-modulus", "-noout", "-in", str(cert_file)]) | |
modulus_bytes = bytearray.fromhex(proc.stdout.split(b"Modulus=")[1].decode()) | |
key_bits = len(modulus_bytes) * 8 | |
if key_bits != 2048: | |
raise Exception("Invalid modulus length") | |
key_qwords = len(modulus_bytes) // 8 | |
N = int.from_bytes(modulus_bytes, byteorder="big") | |
B = 2 ** 64 | |
N0_inv = B - pow(N, -1, B) | |
if N0_inv > 0xFFFFFFFFFFFFFFFF: | |
raise Exception("Invalid N0 inverse") | |
R = 2 ** key_bits | |
R_2_mod_N = pow(R, 2, N) | |
rsa_pub_key = bytearray() | |
# OC_RSA_PUBLIC_KEY_2048 | |
# NumQwords | |
rsa_pub_key.extend(key_qwords.to_bytes(2, byteorder="little")) | |
# Reserved | |
rsa_pub_key.extend(int(0).to_bytes(6, byteorder="little")) | |
# N0Inv | |
rsa_pub_key.extend(N0_inv.to_bytes(8, byteorder="little")) | |
# Modulus | |
rsa_pub_key.extend(N.to_bytes(len(modulus_bytes), byteorder="little")) | |
# RSqrMod | |
rsa_pub_key.extend(R_2_mod_N.to_bytes(len(modulus_bytes), byteorder="little")) | |
return rsa_pub_key | |
def sign_oc(oc_path: pathlib.Path, key: str, cert_file: pathlib.Path, engine: typing.Union[str, None]) -> None: | |
# Sign all EFI binaries in EFI/OC (excluding OpenCore.efi, as this may be binpatched during vaulting). | |
oc_files = sorted(filter(lambda file: file.is_file() and file.name[0] != "." and file.name != "OpenCore.efi" and | |
file.stem != "vault", oc_path.rglob("*"))) | |
efi_files = filter(lambda file: file.suffix == ".efi" and file.name != "OpenCore.efi", oc_files) | |
for file in efi_files: | |
print("Signing", str(file.relative_to(oc_path))) | |
sign_efi_binary(file, key, cert_file, engine) | |
# Generate vault.plist from all (unhidden) files in EFI/OC. | |
print("Vaulting OC") | |
vault = {"Files": {}, "Version": 1} | |
for file in oc_files: | |
with open(file, "rb") as fp: | |
data = fp.read() | |
h = hashlib.sha256() | |
h.update(data) | |
file_path = str(pathlib.PureWindowsPath(file.relative_to(oc_path))) | |
vault["Files"][file_path] = h.digest() | |
vault_path = oc_path / "vault.plist" | |
with open(vault_path, "wb") as fp: | |
plistlib.dump(vault, fp) | |
# Sign vault.plist. | |
vault_sig_path = oc_path / "vault.sig" | |
openssl_gen_signed_digest(vault_path, key, cert_file, vault_sig_path, engine) | |
# Binpatch OpenCore.efi to embed the public key used for vault.sig. | |
pub_key = gen_oc_rsa_pub_key(cert_file) | |
oc_binary_path = oc_path / "OpenCore.efi" | |
with open(oc_binary_path, "rb") as fp: | |
oc_binary = fp.read() | |
oc_binary_pub_key_start = oc_binary.find(b"=BEGIN OC VAULT=") + len(b"=BEGIN OC VAULT=") | |
oc_binary_pub_key_end = oc_binary.find(b"==END OC VAULT==") | |
if oc_binary_pub_key_end - oc_binary_pub_key_start != len(pub_key): | |
print("Invalid vault magic in OpenCore.efi", file=sys.stder) | |
sys.exit(1) | |
oc_binary = oc_binary[:oc_binary_pub_key_start] + pub_key + oc_binary[oc_binary_pub_key_end:] | |
with open(oc_binary_path, "wb") as fp: | |
fp.write(oc_binary) | |
# Finally sign OpenCore.efi. | |
print("Signing OpenCore.efi") | |
sign_efi_binary(oc_binary_path, key, cert_file, engine) | |
def check_file(file: pathlib.Path) -> None: | |
if not file.exists(): | |
print(str(file), "does not exist", file=sys.stderr) | |
sys.exit(1) | |
def find_executable(name: str) -> str: | |
path = shutil.which(name) | |
if not path: | |
print(name, "was not found in PATH", file=sys.stderr) | |
sys.exit(1) | |
return path | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description="OpenCore UEFI Secure Boot/vault signing utility") | |
parser.add_argument("-e", "--engine", type=str, help="OpenSSL engine for key operations") | |
parser.add_argument("-k", "--key", type=str, required=True, help="Path to signing key (or key URI for engines)") | |
parser.add_argument("-c", "--cert", type=pathlib.Path, required=True, help="Path to certificate") | |
parser.add_argument("oc-path", type=pathlib.Path, help="Path to OpenCore (EFI/OC)") | |
args = parser.parse_args() | |
check_file(args.cert) | |
oc_path = getattr(args, "oc-path") | |
if not oc_path.exists() or not (oc_path / "OpenCore.efi").exists(): | |
print("OpenCore.efi was not found in", str(oc_path), file=sys.stderr) | |
sys.exit(1) | |
openssl_path = find_executable("openssl") | |
sbattach_path = find_executable("sbattach") | |
sbsign_path = find_executable("sbsign") | |
sbverify_path = find_executable("sbverify") | |
sign_oc(oc_path, args.key, args.cert, args.engine) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# References: | |
# https://habr.com/ru/post/273497/ | |
# https://wiki.gentoo.org/wiki/User:Sakaki/Sakaki%27s_EFI_Install_Guide/Configuring_Secure_Boot_under_OpenRC | |
# https://jade.fyi/blog/tpm-ssh/ | |
# https://incenp.org/notes/2020/tpm-based-ssh-key.html | |
# https://www.evolware.org/?p=597 | |
# Silence FAPI warnings | |
export TSS2_LOG=fapi+NONE | |
# Initialize store | |
tpm2_ptool init | |
# Generate Secure Boot keys (PK, KEK, ISK) | |
tpm2_ptool addtoken --pid 1 --label PK --sopin $PK_PIN --userpin $PK_PIN | |
tpm2_ptool addtoken --pid 1 --label KEK --sopin $KEK_PIN --userpin $KEK_PIN | |
tpm2_ptool addtoken --pid 1 --label ISK --sopin $ISK_PIN --userpin $ISK_PIN | |
tpm2_ptool addkey --algorithm rsa2048 --label PK --userpin $PK_PIN | |
tpm2_ptool addkey --algorithm rsa2048 --label KEK --userpin $KEK_PIN | |
tpm2_ptool addkey --algorithm rsa2048 --label ISK --userpin $ISK_PIN | |
# Obtain public key certificates | |
openssl req -new -x509 -newkey rsa:2048 -sha256 -days 1825 \ | |
-subj "/CN=Platform Key" -engine pkcs11 -keyform engine \ | |
-key "pkcs11:token=PK;pin-value=$PK_PIN" -out PK.pem | |
openssl req -new -x509 -newkey rsa:2048 -sha256 -days 1825 \ | |
-subj "/CN=Key Exchange Key" -engine pkcs11 -keyform engine \ | |
-key "pkcs11:token=KEK;pin-value=$KEK_PIN" -out KEK.pem | |
openssl req -new -x509 -newkey rsa:2048 -sha256 -days 1825 \ | |
-subj "/CN=Image Signing Key" -engine pkcs11 -keyform engine \ | |
-key "pkcs11:token=ISK;pin-value=$ISK_PIN" -out ISK.pem | |
# Download MS UEFI certs (DB) and revocation list (DBX) | |
# Add other certs if needed (e.g. FedoraSecureBootCA.pem) | |
curl -O 'https://uefi.org/sites/default/files/resources/dbxupdate_x64.bin' | |
curl -L 'https://go.microsoft.com/fwlink/p/?linkid=321192' \ | |
-o MicWinProPCA2011_2011-10-19.crt | |
curl -L 'https://go.microsoft.com/fwlink/p/?linkid=321194' \ | |
-o MicCorUEFCA2011_2011-06-27.crt | |
# Generate authenticated variables | |
# Copy .auth files to to ESP or some other volume accessible in setup interface | |
uuidgen > owner_guid.txt | |
python3 authvargen.py -e pkcs11 -g $(< owner_guid.txt) \ | |
--pk "pkcs11:token=PK;pin-value=$PK_PIN" --pk-cert PK.pem \ | |
--kek "pkcs11:token=KEK;pin-value=$KEK_PIN" --kek-cert KEK.pem \ | |
--dbx dbxupdate_x64.bin -o . ISK.pem FedoraSecureBootCA.pem \ | |
MicWinProPCA2011_2011-10-19.crt MicCorUEFCA2011_2011-06-27.crt | |
# Sign and vault OpenCore | |
python3 ocsign.py -e pkcs11 -k "pkcs11:token=ISK;pin-value=$ISK_PIN" \ | |
-c ISK.pem /path/to/EFI/OC | |
# Bonus: Use DKMS sign helper to sign Linux kernel modules | |
# Link store in root's home and copy ISK public key certificate (as DER) | |
sudo ln -s "$HOME/.tpm2_pkcs11" /root | |
sudo openssl x509 -in ISK.pem -out /root/ISK.der -outform DER | |
# Update DKMS sign helper to use ISK | |
sudo mv /etc/dkms/sign_helper.sh /etc/dkms/sign_helper.sh.bak | |
sudo tee /etc/dkms/sign_helper.sh <<'EOF' | |
#!/usr/bin/sh | |
/lib/modules/"$1"/build/scripts/sign-file sha512 "pkcs11:token=ISK" /root/ISK.der "$2" | |
'EOF' | |
# TODO: reinstall DKMS modules | |
# Reboot into setup interface, enable secure boot, and enroll authenticated | |
# variables |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# References: | |
# https://habr.com/ru/post/273497/ | |
# https://wiki.gentoo.org/wiki/User:Sakaki/Sakaki%27s_EFI_Install_Guide/Configuring_Secure_Boot_under_OpenRC | |
# Generate Secure Boot keys (PK, KEK, ISK) | |
openssl req -new -x509 -newkey rsa:2048 -sha256 -days 1825 \ | |
-subj "/CN=Platform Key" -keyout PK.key -out PK.pem | |
openssl req -new -x509 -newkey rsa:2048 -sha256 -days 1825 \ | |
-subj "/CN=Key Exchange Key" -keyout KEK.key -out KEK.pem | |
openssl req -new -x509 -newkey rsa:2048 -sha256 -days 1825 \ | |
-subj "/CN=Image Signing Key" -keyout ISK.key -out ISK.pem | |
# Download MS UEFI certs (DB) and revocation list (DBX) | |
# Add other certs if needed (e.g. FedoraSecureBootCA.pem) | |
curl -O 'https://uefi.org/sites/default/files/resources/dbxupdate_x64.bin' | |
curl -L 'https://go.microsoft.com/fwlink/p/?linkid=321192' \ | |
-o MicWinProPCA2011_2011-10-19.crt | |
curl -L 'https://go.microsoft.com/fwlink/p/?linkid=321194' \ | |
-o MicCorUEFCA2011_2011-06-27.crt | |
# Generate authenticated variables | |
# Copy .auth files to to ESP or some other volume accessible in setup interface | |
uuidgen > owner_guid.txt | |
python3 authvargen.py -g $(< owner_guid.txt) --pk PK.key --pk-cert PK.pem \ | |
--kek KEK.key --kek-cert KEK.pem --dbx dbxupdate_x64.bin -o . ISK.pem \ | |
FedoraSecureBootCA.pem MicWinProPCA2011_2011-10-19.crt \ | |
MicCorUEFCA2011_2011-06-27.crt | |
# Sign and vault OpenCore | |
python3 ocsign.py -k ISK.key -c ISK.pem /path/to/EFI/OC | |
# Reboot into setup interface, enable secure boot, and enroll authenticated | |
# variables |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment