2016-09-01 11:02:43 +02:00
## @file
# This tool adds EFI_FIRMWARE_IMAGE_AUTHENTICATION for a binary.
#
# This tool only support CertType - EFI_CERT_TYPE_PKCS7_GUID
# {0x4aafd29d, 0x68df, 0x49ee, {0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7}}
#
# This tool has been tested with OpenSSL.
#
# Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution. The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
'''
Pkcs7Sign
'''
import os
import sys
import argparse
import subprocess
import uuid
import struct
import collections
from Common . BuildVersion import gBUILD_VERSION
#
# Globals for help information
#
__prog__ = ' Pkcs7Sign '
__version__ = ' %s Version %s ' % ( __prog__ , ' 0.9 ' + gBUILD_VERSION )
__copyright__ = ' Copyright (c) 2016, Intel Corporation. All rights reserved. '
__usage__ = ' %s -e|-d [options] <input_file> ' % ( __prog__ )
#
# GUID for PKCS7 from UEFI Specification
#
WIN_CERT_REVISION = 0x0200
WIN_CERT_TYPE_EFI_GUID = 0x0EF1
EFI_CERT_TYPE_PKCS7_GUID = uuid . UUID ( ' { 4aafd29d-68df-49ee-8aa9-347d375665a7} ' )
#
# typedef struct _WIN_CERTIFICATE {
# UINT32 dwLength;
# UINT16 wRevision;
# UINT16 wCertificateType;
# //UINT8 bCertificate[ANYSIZE_ARRAY];
# } WIN_CERTIFICATE;
#
# typedef struct _WIN_CERTIFICATE_UEFI_GUID {
# WIN_CERTIFICATE Hdr;
# EFI_GUID CertType;
# //UINT8 CertData[ANYSIZE_ARRAY];
# } WIN_CERTIFICATE_UEFI_GUID;
#
# typedef struct {
# UINT64 MonotonicCount;
# WIN_CERTIFICATE_UEFI_GUID AuthInfo;
# } EFI_FIRMWARE_IMAGE_AUTHENTICATION;
#
#
# Filename of test signing private cert that is stored in same directory as this tool
#
TEST_SIGNER_PRIVATE_CERT_FILENAME = ' TestCert.pem '
TEST_OTHER_PUBLIC_CERT_FILENAME = ' TestSub.pub.pem '
TEST_TRUSTED_PUBLIC_CERT_FILENAME = ' TestRoot.pub.pem '
if __name__ == ' __main__ ' :
#
# Create command line argument parser object
#
parser = argparse . ArgumentParser ( prog = __prog__ , version = __version__ , usage = __usage__ , description = __copyright__ , conflict_handler = ' resolve ' )
group = parser . add_mutually_exclusive_group ( required = True )
group . add_argument ( " -e " , action = " store_true " , dest = ' Encode ' , help = ' encode file ' )
group . add_argument ( " -d " , action = " store_true " , dest = ' Decode ' , help = ' decode file ' )
parser . add_argument ( " -o " , " --output " , dest = ' OutputFile ' , type = str , metavar = ' filename ' , help = " specify the output filename " , required = True )
parser . add_argument ( " --signer-private-cert " , dest = ' SignerPrivateCertFile ' , type = argparse . FileType ( ' rb ' ) , help = " specify the signer private cert filename. If not specified, a test signer private cert is used. " )
parser . add_argument ( " --other-public-cert " , dest = ' OtherPublicCertFile ' , type = argparse . FileType ( ' rb ' ) , help = " specify the other public cert filename. If not specified, a test other public cert is used. " )
parser . add_argument ( " --trusted-public-cert " , dest = ' TrustedPublicCertFile ' , type = argparse . FileType ( ' rb ' ) , help = " specify the trusted public cert filename. If not specified, a test trusted public cert is used. " )
parser . add_argument ( " --monotonic-count " , dest = ' MonotonicCountStr ' , type = str , help = " specify the MonotonicCount in FMP capsule. If not specified, 0 is used. " )
parser . add_argument ( " --signature-size " , dest = ' SignatureSizeStr ' , type = str , help = " specify the signature size for decode process. " )
parser . add_argument ( " -v " , " --verbose " , dest = ' Verbose ' , action = " store_true " , help = " increase output messages " )
parser . add_argument ( " -q " , " --quiet " , dest = ' Quiet ' , action = " store_true " , help = " reduce output messages " )
parser . add_argument ( " --debug " , dest = ' Debug ' , type = int , metavar = ' [0-9] ' , choices = range ( 0 , 10 ) , default = 0 , help = " set debug level " )
parser . add_argument ( metavar = " input_file " , dest = ' InputFile ' , type = argparse . FileType ( ' rb ' ) , help = " specify the input filename " )
#
# Parse command line arguments
#
args = parser . parse_args ( )
#
# Generate file path to Open SSL command
#
OpenSslCommand = ' openssl '
try :
OpenSslPath = os . environ [ ' OPENSSL_PATH ' ]
OpenSslCommand = os . path . join ( OpenSslPath , OpenSslCommand )
except :
pass
#
# Verify that Open SSL command is available
#
try :
Process = subprocess . Popen ( ' %s version ' % ( OpenSslCommand ) , stdout = subprocess . PIPE , stderr = subprocess . PIPE )
except :
print ' ERROR: Open SSL command not available. Please verify PATH or set OPENSSL_PATH '
sys . exit ( 1 )
Version = Process . communicate ( )
if Process . returncode < > 0 :
print ' ERROR: Open SSL command not available. Please verify PATH or set OPENSSL_PATH '
sys . exit ( Process . returncode )
print Version [ 0 ]
#
# Read input file into a buffer and save input filename
#
args . InputFileName = args . InputFile . name
args . InputFileBuffer = args . InputFile . read ( )
args . InputFile . close ( )
#
# Save output filename and check if path exists
#
OutputDir = os . path . dirname ( args . OutputFile )
if not os . path . exists ( OutputDir ) :
print ' ERROR: The output path does not exist: %s ' % OutputDir
sys . exit ( 1 )
args . OutputFileName = args . OutputFile
try :
if args . MonotonicCountStr . upper ( ) . startswith ( ' 0X ' ) :
args . MonotonicCountValue = ( long ) ( args . MonotonicCountStr , 16 )
else :
args . MonotonicCountValue = ( long ) ( args . MonotonicCountStr )
except :
args . MonotonicCountValue = ( long ) ( 0 )
if args . Encode :
#
# Save signer private cert filename and close private cert file
#
try :
args . SignerPrivateCertFileName = args . SignerPrivateCertFile . name
args . SignerPrivateCertFile . close ( )
except :
try :
#
# Get path to currently executing script or executable
#
if hasattr ( sys , ' frozen ' ) :
Pkcs7ToolPath = sys . executable
else :
Pkcs7ToolPath = sys . argv [ 0 ]
if Pkcs7ToolPath . startswith ( ' " ' ) :
Pkcs7ToolPath = Pkcs7ToolPath [ 1 : ]
if Pkcs7ToolPath . endswith ( ' " ' ) :
Pkcs7ToolPath = RsaToolPath [ : - 1 ]
args . SignerPrivateCertFileName = os . path . join ( os . path . dirname ( os . path . realpath ( Pkcs7ToolPath ) ) , TEST_SIGNER_PRIVATE_CERT_FILENAME )
args . SignerPrivateCertFile = open ( args . SignerPrivateCertFileName , ' rb ' )
args . SignerPrivateCertFile . close ( )
except :
print ' ERROR: test signer private cert file %s missing ' % ( args . SignerPrivateCertFileName )
sys . exit ( 1 )
#
# Save other public cert filename and close public cert file
#
try :
args . OtherPublicCertFileName = args . OtherPublicCertFile . name
args . OtherPublicCertFile . close ( )
except :
try :
#
# Get path to currently executing script or executable
#
if hasattr ( sys , ' frozen ' ) :
Pkcs7ToolPath = sys . executable
else :
Pkcs7ToolPath = sys . argv [ 0 ]
if Pkcs7ToolPath . startswith ( ' " ' ) :
Pkcs7ToolPath = Pkcs7ToolPath [ 1 : ]
if Pkcs7ToolPath . endswith ( ' " ' ) :
Pkcs7ToolPath = RsaToolPath [ : - 1 ]
args . OtherPublicCertFileName = os . path . join ( os . path . dirname ( os . path . realpath ( Pkcs7ToolPath ) ) , TEST_OTHER_PUBLIC_CERT_FILENAME )
args . OtherPublicCertFile = open ( args . OtherPublicCertFileName , ' rb ' )
args . OtherPublicCertFile . close ( )
except :
print ' ERROR: test other public cert file %s missing ' % ( args . OtherPublicCertFileName )
sys . exit ( 1 )
2016-10-13 09:59:06 +02:00
format = " %d sQ " % len ( args . InputFileBuffer )
FullInputFileBuffer = struct . pack ( format , args . InputFileBuffer , args . MonotonicCountValue )
2016-09-01 11:02:43 +02:00
#
# Sign the input file using the specified private key and capture signature from STDOUT
#
Process = subprocess . Popen ( ' %s smime -sign -binary -signer " %s " -outform DER -md sha256 -certfile " %s " ' % ( OpenSslCommand , args . SignerPrivateCertFileName , args . OtherPublicCertFileName ) , stdin = subprocess . PIPE , stdout = subprocess . PIPE , stderr = subprocess . PIPE )
Signature = Process . communicate ( input = FullInputFileBuffer ) [ 0 ]
if Process . returncode < > 0 :
sys . exit ( Process . returncode )
#
# Write output file that contains Signature, and Input data
#
args . OutputFile = open ( args . OutputFileName , ' wb ' )
args . OutputFile . write ( Signature )
args . OutputFile . write ( args . InputFileBuffer )
args . OutputFile . close ( )
if args . Decode :
#
# Save trusted public cert filename and close public cert file
#
try :
args . TrustedPublicCertFileName = args . TrustedPublicCertFile . name
args . TrustedPublicCertFile . close ( )
except :
try :
#
# Get path to currently executing script or executable
#
if hasattr ( sys , ' frozen ' ) :
Pkcs7ToolPath = sys . executable
else :
Pkcs7ToolPath = sys . argv [ 0 ]
if Pkcs7ToolPath . startswith ( ' " ' ) :
Pkcs7ToolPath = Pkcs7ToolPath [ 1 : ]
if Pkcs7ToolPath . endswith ( ' " ' ) :
Pkcs7ToolPath = RsaToolPath [ : - 1 ]
args . TrustedPublicCertFileName = os . path . join ( os . path . dirname ( os . path . realpath ( Pkcs7ToolPath ) ) , TEST_TRUSTED_PUBLIC_CERT_FILENAME )
args . TrustedPublicCertFile = open ( args . TrustedPublicCertFileName , ' rb ' )
args . TrustedPublicCertFile . close ( )
except :
print ' ERROR: test trusted public cert file %s missing ' % ( args . TrustedPublicCertFileName )
sys . exit ( 1 )
if not args . SignatureSizeStr :
print " ERROR: please use the option --signature-size to specify the size of the signature data! "
sys . exit ( 1 )
else :
if args . SignatureSizeStr . upper ( ) . startswith ( ' 0X ' ) :
SignatureSize = ( long ) ( args . SignatureSizeStr , 16 )
else :
SignatureSize = ( long ) ( args . SignatureSizeStr )
if SignatureSize < 0 :
print " ERROR: The value of option --signature-size can ' t be set to negative value! "
sys . exit ( 1 )
elif SignatureSize > len ( args . InputFileBuffer ) :
print " ERROR: The value of option --signature-size is exceed the size of the input file ! "
sys . exit ( 1 )
args . SignatureBuffer = args . InputFileBuffer [ 0 : SignatureSize ]
args . InputFileBuffer = args . InputFileBuffer [ SignatureSize : ]
2016-10-13 09:59:06 +02:00
format = " %d sQ " % len ( args . InputFileBuffer )
FullInputFileBuffer = struct . pack ( format , args . InputFileBuffer , args . MonotonicCountValue )
2016-09-01 11:02:43 +02:00
#
# Save output file contents from input file
#
open ( args . OutputFileName , ' wb ' ) . write ( FullInputFileBuffer )
#
# Verify signature
#
Process = subprocess . Popen ( ' %s smime -verify -inform DER -content %s -CAfile %s ' % ( OpenSslCommand , args . OutputFileName , args . TrustedPublicCertFileName ) , stdin = subprocess . PIPE , stdout = subprocess . PIPE , stderr = subprocess . PIPE )
Process . communicate ( input = args . SignatureBuffer ) [ 0 ]
if Process . returncode < > 0 :
print ' ERROR: Verification failed '
os . remove ( args . OutputFileName )
sys . exit ( Process . returncode )
open ( args . OutputFileName , ' wb ' ) . write ( args . InputFileBuffer )