2 # This tool encodes and decodes GUIDed FFS sections for a GUID type of
3 # EFI_CERT_TYPE_RSA2048_SHA256_GUID defined in the UEFI 2.4 Specification as
4 # {0xa7717414, 0xc616, 0x4977, {0x94, 0x20, 0x84, 0x47, 0x12, 0xa7, 0x35, 0xbf}}
5 # This tool has been tested with OpenSSL 1.0.1e 11 Feb 2013
7 # Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
8 # This program and the accompanying materials
9 # are licensed and made available under the terms and conditions of the BSD License
10 # which accompanies this distribution. The full text of the license may be found at
11 # http://opensource.org/licenses/bsd-license.php
13 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
30 # GUID for SHA 256 Hash Algorithm from UEFI Specification
32 EFI_HASH_ALGORITHM_SHA256_GUID
= uuid
.UUID('{51aa59de-fdf2-4ea3-bc63-875fb7842ee9}')
35 # Structure defintion to unpack EFI_CERT_BLOCK_RSA_2048_SHA256 from UEFI 2.4 Specification
37 # typedef struct _EFI_CERT_BLOCK_RSA_2048_SHA256 {
39 # UINT8 PublicKey[256];
40 # UINT8 Signature[256];
41 # } EFI_CERT_BLOCK_RSA_2048_SHA256;
43 EFI_CERT_BLOCK_RSA_2048_SHA256
= collections
.namedtuple('EFI_CERT_BLOCK_RSA_2048_SHA256', ['HashType','PublicKey','Signature'])
44 EFI_CERT_BLOCK_RSA_2048_SHA256_STRUCT
= struct
.Struct('16s256s256s')
47 # Filename of test signing private key that is stored in same directory as this tool
49 TEST_SIGNING_PRIVATE_KEY_FILENAME
= 'TestSigningPrivateKey.pem'
51 if __name__
== '__main__':
53 # Save name of the program
55 ProgramName
= sys
.argv
[0]
60 print '%s - Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.' % (ProgramName
)
63 # Create command line argument parser object
65 parser
= argparse
.ArgumentParser(prog
=ProgramName
, usage
='%(prog)s -e|-d [options] <input_file>', add_help
=False)
66 group
= parser
.add_mutually_exclusive_group(required
=True)
67 group
.add_argument("-e", action
="store_true", dest
='Encode', help='encode file')
68 group
.add_argument("-d", action
="store_true", dest
='Decode', help='decode file')
69 parser
.add_argument("-o", "--output", dest
='OutputFile', type=argparse
.FileType('wb'), metavar
='filename', help="specify the output filename", required
=True)
70 parser
.add_argument("--private-key", dest
='PrivateKeyFile', type=argparse
.FileType('rb'), help="specify the private key filename. If not specified, a test signing key is used.")
71 parser
.add_argument("-v", "--verbose", dest
='Verbose', action
="store_true", help="increase output messages")
72 parser
.add_argument("-q", "--quiet", dest
='Quiet', action
="store_true", help="reduce output messages")
73 parser
.add_argument("--debug", dest
='Debug', type=int, metavar
='[0-9]', choices
=range(0,10), default
=0, help="set debug level")
74 parser
.add_argument("--version", dest
='Version', action
="store_true", help="display the program version and exit")
75 parser
.add_argument("-h", "--help", dest
='Help', action
="help", help="display this help text")
76 parser
.add_argument(metavar
="input_file", dest
='InputFile', type=argparse
.FileType('rb'), help="specify the input filename")
79 # Parse command line arguments
81 args
= parser
.parse_args()
84 # Generate file path to Open SSL command
86 OpenSslCommand
= 'openssl'
88 OpenSslPath
= os
.environ
['OPENSSL_PATH']
89 OpenSslCommand
= os
.path
.join(OpenSslPath
, OpenSslCommand
)
94 # Verify that Open SSL command is available
97 Process
= subprocess
.Popen('%s version' % (OpenSslCommand
), stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)
99 print 'ERROR: Open SSL command not available. Please verify PATH or set OPENSSL_PATH'
102 Version
= Process
.communicate()
103 if Process
.returncode
<> 0:
104 print 'ERROR: Open SSL command not available. Please verify PATH or set OPENSSL_PATH'
105 sys
.exit(Process
.returncode
)
109 # Read input file into a buffer and save input filename
111 args
.InputFileName
= args
.InputFile
.name
112 args
.InputFileBuffer
= args
.InputFile
.read()
113 args
.InputFile
.close()
116 # Save output filename and close output file
118 args
.OutputFileName
= args
.OutputFile
.name
119 args
.OutputFile
.close()
122 # Save private key filename and close private key file
125 args
.PrivateKeyFileName
= args
.PrivateKeyFile
.name
126 args
.PrivateKeyFile
.close()
130 # Get path to currently executing script or executable
132 if hasattr(sys
, 'frozen'):
133 RsaToolPath
= sys
.executable
135 RsaToolPath
= sys
.argv
[0]
136 if RsaToolPath
.startswith('"'):
137 RsaToolPath
= RsaToolPath
[1:]
138 if RsaToolPath
.endswith('"'):
139 RsaToolPath
= RsaToolPath
[:-1]
140 args
.PrivateKeyFileName
= os
.path
.join(os
.path
.dirname(os
.path
.realpath(RsaToolPath
)), TEST_SIGNING_PRIVATE_KEY_FILENAME
)
141 args
.PrivateKeyFile
= open(args
.PrivateKeyFileName
, 'rb')
142 args
.PrivateKeyFile
.close()
144 print 'ERROR: test signing private key file %s missing' % (args
.PrivateKeyFileName
)
148 # Extract public key from private key into STDOUT
150 Process
= subprocess
.Popen('%s rsa -in "%s" -modulus -noout' % (OpenSslCommand
, args
.PrivateKeyFileName
), stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)
151 PublicKeyHexString
= Process
.communicate()[0].split('=')[1].strip()
153 while len(PublicKeyHexString
) > 0:
154 PublicKey
= PublicKey
+ chr(int(PublicKeyHexString
[0:2],16))
155 PublicKeyHexString
=PublicKeyHexString
[2:]
156 if Process
.returncode
<> 0:
157 sys
.exit(Process
.returncode
)
161 # Sign the input file using the specified private key and capture signature from STDOUT
163 Process
= subprocess
.Popen('%s sha256 -sign "%s"' % (OpenSslCommand
, args
.PrivateKeyFileName
), stdin
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)
164 Signature
= Process
.communicate(input=args
.InputFileBuffer
)[0]
165 if Process
.returncode
<> 0:
166 sys
.exit(Process
.returncode
)
169 # Write output file that contains hash GUID, Public Key, Signature, and Input data
171 args
.OutputFile
= open(args
.OutputFileName
, 'wb')
172 args
.OutputFile
.write(EFI_HASH_ALGORITHM_SHA256_GUID
.get_bytes_le())
173 args
.OutputFile
.write(PublicKey
)
174 args
.OutputFile
.write(Signature
)
175 args
.OutputFile
.write(args
.InputFileBuffer
)
176 args
.OutputFile
.close()
180 # Parse Hash Type, Public Key, and Signature from the section header
182 Header
= EFI_CERT_BLOCK_RSA_2048_SHA256
._make(EFI_CERT_BLOCK_RSA_2048_SHA256_STRUCT
.unpack_from(args
.InputFileBuffer
))
183 args
.InputFileBuffer
= args
.InputFileBuffer
[EFI_CERT_BLOCK_RSA_2048_SHA256_STRUCT
.size
:]
186 # Verify that the Hash Type matches the expected SHA256 type
188 if uuid
.UUID(bytes_le
= Header
.HashType
) <> EFI_HASH_ALGORITHM_SHA256_GUID
:
189 print 'ERROR: unsupport hash GUID'
193 # Verify the public key
195 if Header
.PublicKey
<> PublicKey
:
196 print 'ERROR: Public key in input file does not match public key from private key file'
200 # Write Signature to output file
202 open(args
.OutputFileName
, 'wb').write(Header
.Signature
)
207 Process
= subprocess
.Popen('%s sha256 -prverify "%s" -signature %s' % (OpenSslCommand
, args
.PrivateKeyFileName
, args
.OutputFileName
), stdin
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
)
208 Process
.communicate(args
.InputFileBuffer
)
209 if Process
.returncode
<> 0:
210 print 'ERROR: Verification failed'
211 os
.remove (args
.OutputFileName
)
212 sys
.exit(Process
.returncode
)
215 # Save output file contents from input file
217 open(args
.OutputFileName
, 'wb').write(args
.InputFileBuffer
)