2016-09-01 17:02:43 +08: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.
#
2017-03-13 15:27:17 +08:00
# Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>
2019-04-03 16:03:11 -07:00
# SPDX-License-Identifier: BSD-2-Clause-Patent
2016-09-01 17:02:43 +08:00
#
'''
Pkcs7Sign
'''
2018-10-15 08:27:53 +08:00
from __future__ import print_function
2016-09-01 17:02:43 +08:00
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
#
2018-12-17 00:14:51 +08:00
parser = argparse . ArgumentParser ( prog = __prog__ , usage = __usage__ , description = __copyright__ , conflict_handler = 'resolve' )
2016-09-01 17:02:43 +08:00
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' )
2018-12-17 00:14:51 +08:00
group . add_argument ( "--version" , action = 'version' , version = __version__ )
2016-09-01 17:02:43 +08:00
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" )
2018-06-25 18:31:33 +08:00
parser . add_argument ( "--debug" , dest = 'Debug' , type = int , metavar = '[0-9]' , choices = range ( 0 , 10 ), default = 0 , help = "set debug level" )
2016-09-01 17:02:43 +08:00
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 )
2017-04-19 17:51:45 +08:00
if ' ' in OpenSslCommand :
OpenSslCommand = '"' + OpenSslCommand + '"'
2016-09-01 17:02:43 +08:00
except :
pass
#
# Verify that Open SSL command is available
#
try :
2017-03-13 15:27:17 +08:00
Process = subprocess . Popen ( ' %s version' % ( OpenSslCommand ), stdout = subprocess . PIPE , stderr = subprocess . PIPE , shell = True )
2016-09-01 17:02:43 +08:00
except :
2018-06-25 18:31:26 +08:00
print ( 'ERROR: Open SSL command not available. Please verify PATH or set OPENSSL_PATH' )
2016-09-01 17:02:43 +08:00
sys . exit ( 1 )
Version = Process . communicate ()
2018-06-25 18:31:27 +08:00
if Process . returncode != 0 :
2018-06-25 18:31:26 +08:00
print ( 'ERROR: Open SSL command not available. Please verify PATH or set OPENSSL_PATH' )
2016-09-01 17:02:43 +08:00
sys . exit ( Process . returncode )
2019-07-16 17:14:08 +08:00
print ( Version [ 0 ] . decode ())
2016-09-01 17:02:43 +08:00
#
# 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 ):
2018-06-25 18:31:26 +08:00
print ( 'ERROR: The output path does not exist: %s ' % OutputDir )
2016-09-01 17:02:43 +08:00
sys . exit ( 1 )
args . OutputFileName = args . OutputFile
try :
if args . MonotonicCountStr . upper () . startswith ( '0X' ):
2018-12-03 10:29:40 +08:00
args . MonotonicCountValue = int ( args . MonotonicCountStr , 16 )
2016-09-01 17:02:43 +08:00
else :
2018-12-03 10:29:40 +08:00
args . MonotonicCountValue = int ( args . MonotonicCountStr )
2016-09-01 17:02:43 +08:00
except :
2018-12-03 10:29:40 +08:00
args . MonotonicCountValue = int ( 0 )
2016-09-01 17:02:43 +08:00
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 :
2018-06-25 18:31:26 +08:00
print ( 'ERROR: test signer private cert file %s missing' % ( args . SignerPrivateCertFileName ))
2016-09-01 17:02:43 +08:00
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 :
2018-06-25 18:31:26 +08:00
print ( 'ERROR: test other public cert file %s missing' % ( args . OtherPublicCertFileName ))
2016-09-01 17:02:43 +08:00
sys . exit ( 1 )
2016-10-13 15:59:06 +08:00
format = " %d sQ" % len ( args . InputFileBuffer )
FullInputFileBuffer = struct . pack ( format , args . InputFileBuffer , args . MonotonicCountValue )
2016-09-01 17:02:43 +08:00
#
# Sign the input file using the specified private key and capture signature from STDOUT
#
2017-03-28 15:04:13 +08:00
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 , shell = True )
2016-09-01 17:02:43 +08:00
Signature = Process . communicate ( input = FullInputFileBuffer )[ 0 ]
2018-06-25 18:31:27 +08:00
if Process . returncode != 0 :
2016-09-01 17:02:43 +08:00
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 :
2018-06-25 18:31:26 +08:00
print ( 'ERROR: test trusted public cert file %s missing' % ( args . TrustedPublicCertFileName ))
2016-09-01 17:02:43 +08:00
sys . exit ( 1 )
if not args . SignatureSizeStr :
2018-06-25 18:31:26 +08:00
print ( "ERROR: please use the option --signature-size to specify the size of the signature data!" )
2016-09-01 17:02:43 +08:00
sys . exit ( 1 )
else :
if args . SignatureSizeStr . upper () . startswith ( '0X' ):
2018-12-03 10:29:40 +08:00
SignatureSize = int ( args . SignatureSizeStr , 16 )
2016-09-01 17:02:43 +08:00
else :
2018-12-03 10:29:40 +08:00
SignatureSize = int ( args . SignatureSizeStr )
2016-09-01 17:02:43 +08:00
if SignatureSize < 0 :
2018-06-25 18:31:26 +08:00
print ( "ERROR: The value of option --signature-size can't be set to negative value!" )
2016-09-01 17:02:43 +08:00
sys . exit ( 1 )
elif SignatureSize > len ( args . InputFileBuffer ):
2018-06-25 18:31:26 +08:00
print ( "ERROR: The value of option --signature-size is exceed the size of the input file !" )
2016-09-01 17:02:43 +08:00
sys . exit ( 1 )
args . SignatureBuffer = args . InputFileBuffer [ 0 : SignatureSize ]
args . InputFileBuffer = args . InputFileBuffer [ SignatureSize :]
2016-10-13 15:59:06 +08:00
format = " %d sQ" % len ( args . InputFileBuffer )
FullInputFileBuffer = struct . pack ( format , args . InputFileBuffer , args . MonotonicCountValue )
2016-09-01 17:02:43 +08:00
#
# Save output file contents from input file
#
open ( args . OutputFileName , 'wb' ) . write ( FullInputFileBuffer )
#
# Verify signature
#
2017-03-28 15:04:13 +08:00
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 , shell = True )
2016-09-01 17:02:43 +08:00
Process . communicate ( input = args . SignatureBuffer )[ 0 ]
2018-06-25 18:31:27 +08:00
if Process . returncode != 0 :
2018-06-25 18:31:26 +08:00
print ( 'ERROR: Verification failed' )
2016-09-01 17:02:43 +08:00
os . remove ( args . OutputFileName )
sys . exit ( Process . returncode )
open ( args . OutputFileName , 'wb' ) . write ( args . InputFileBuffer )