From aae89b7072fa1e59a2b605b6dfd6031560877384 Mon Sep 17 00:00:00 2001 From: xumia <59720581+xumia@users.noreply.github.com> Date: Tue, 30 Jun 2020 17:29:51 +0800 Subject: [PATCH] Suppport to verify aboot swi image for secure boot (#969) * Suppport to verify aboot swi image for secure boot * Simplify the code * Fix not return value bug * Add m2crypto to setup.py * Change to only verify the image signed by a correct certificate --- setup.py | 3 +- sonic_installer/bootloader/aboot.py | 50 ++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index adbcc2c992..8bccb843f1 100644 --- a/setup.py +++ b/setup.py @@ -149,7 +149,8 @@ # - tabulate install_requires=[ 'click', - 'natsort' + 'natsort', + 'm2crypto' ], setup_requires= [ 'pytest-runner' diff --git a/sonic_installer/bootloader/aboot.py b/sonic_installer/bootloader/aboot.py index b7c1a061ae..1933921512 100644 --- a/sonic_installer/bootloader/aboot.py +++ b/sonic_installer/bootloader/aboot.py @@ -2,13 +2,18 @@ Bootloader implementation for Aboot used on Arista devices """ +import base64 import collections import os import re import subprocess +import sys +import zipfile import click +from M2Crypto import X509 + from ..common import ( HOST_PATH, IMAGE_DIR_PREFIX, @@ -18,6 +23,12 @@ from .bootloader import Bootloader _secureboot = None + +# For the signature format, see: https://github.com/aristanetworks/swi-tools/tree/master/switools +SWI_SIG_FILE_NAME = 'swi-signature' +SWIX_SIG_FILE_NAME = 'swix-signature' +ISSUERCERT = 'IssuerCert' + def isSecureboot(): global _secureboot if _secureboot is None: @@ -114,11 +125,48 @@ def get_binary_image_version(self, image_path): def verify_binary_image(self, image_path): try: subprocess.check_call(['/usr/bin/unzip', '-tq', image_path]) - # TODO: secureboot check signature + return self._verify_secureboot_image(image_path) except subprocess.CalledProcessError: return False + + def _verify_secureboot_image(self, image_path): + if isSecureboot(): + cert = self.getCert(image_path) + return cert is not None return True + @classmethod + def getCert(cls, swiFile): + with zipfile.ZipFile(swiFile, 'r') as swi: + try: + sigInfo = swi.getinfo(cls.getSigFileName(swiFile)) + except KeyError: + # Occurs if SIG_FILE_NAME is not in the swi (the SWI is not signed properly) + return None + with swi.open(sigInfo, 'r') as sigFile: + for line in sigFile: + data = line.split(':') + if len(data) == 2: + if data[0] == ISSUERCERT: + try: + base64_cert = cls.base64Decode(data[1].strip()) + return X509.load_cert_string(base64_cert) + except TypeError: + return None + else: + sys.stderr.write('Unexpected format for line in swi[x]-signature file: %s\n' % line) + return None + + @classmethod + def getSigFileName(cls, swiFile): + if swiFile.lower().endswith(".swix"): + return SWIX_SIG_FILE_NAME + return SWI_SIG_FILE_NAME + + @classmethod + def base64Decode(cls, text): + return base64.standard_b64decode(text) + @classmethod def detect(cls): with open('/proc/cmdline') as f: