efivalidate 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/FORMAT.md +27 -0
- data/README.md +19 -4
- data/efivalidate.gemspec +6 -3
- data/exe/efivalidate +26 -0
- data/lib/efivalidate/ealf_header.rb +32 -0
- data/lib/efivalidate/ealf_parser.rb +23 -0
- data/lib/efivalidate/ealf_row.rb +30 -0
- data/lib/efivalidate/efi_validation_error.rb +15 -0
- data/lib/efivalidate/efi_validator.rb +35 -0
- data/lib/efivalidate/intel_firmware_descriptor.rb +9 -0
- data/lib/efivalidate/version.rb +2 -2
- data/lib/efivalidate.rb +7 -1
- metadata +40 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 037ca593ce0ba5a08920d051c191e5f9c4d3a71a
|
4
|
+
data.tar.gz: cd59412dc988879399f553b91a965cdca1a59a77
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 156c354cdf536fbc2a09b9a820cf29c023ebdcbd3042241f4286e0689d0b37441ef5b187fea248a6bd218b64d874e726923b2747412356263ce7543457976281
|
7
|
+
data.tar.gz: 8496a21db212539db45a5e6547e361c16a6b25a581855e899ec7c45ad7a73c3a13f543c93d3bba114745709d784cd8e5a218598f294cb288262712ca5513a9de
|
data/.gitignore
CHANGED
data/FORMAT.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
NOTE: In progress
|
3
|
+
|
4
|
+
# Header
|
5
|
+
|
6
|
+
* 0-4: "magic" = "EALF"
|
7
|
+
* 5-8: number of hash rows
|
8
|
+
* 9-12: file size in bytes
|
9
|
+
* 13: ?? Hash function, 00 = sha1, 01 = sha256
|
10
|
+
* Zeros?
|
11
|
+
* 30: UInt16 Offset to hash table
|
12
|
+
* 32: UInt16 Number of Unicode-16 characters following
|
13
|
+
* 34: - NULL = UTF-16 null terminated EFI version string
|
14
|
+
|
15
|
+
# 60 byte "rows"
|
16
|
+
* 1 byte FD region (see `fdutil`)
|
17
|
+
* 0 = FD header / region
|
18
|
+
* 1 = BIOS / EFI
|
19
|
+
* 2 = Intel ME (Code + Data)
|
20
|
+
* 3 = Gigabit Ethernet (Unused)
|
21
|
+
* 4 = Platform Data & Bootloader
|
22
|
+
* 1 byte sub-region
|
23
|
+
* 2 byte index
|
24
|
+
* 4 byte offset
|
25
|
+
* 4 byte size
|
26
|
+
* 16 bytes GUID for section or "FF"
|
27
|
+
* 32 bytes SHA256 for section
|
data/README.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
#
|
1
|
+
# `efivalidate`
|
2
2
|
|
3
|
-
|
3
|
+
Important: See `FORMAT.md`
|
4
4
|
|
5
|
-
|
5
|
+
`efivalidate` is a ruby utility to take a given input EFI payload from macOS and to compare it against
|
6
|
+
Apple's validation schema. Being written in ruby this can occur off-box to ensure that the utility itself
|
7
|
+
hasn't been compromised
|
6
8
|
|
7
9
|
## Installation
|
8
10
|
|
@@ -22,7 +24,20 @@ Or install it yourself as:
|
|
22
24
|
|
23
25
|
## Usage
|
24
26
|
|
25
|
-
|
27
|
+
$ efivalidate
|
28
|
+
|
29
|
+
Usage: efivalidate {input.bin} [PARAMS]
|
30
|
+
|
31
|
+
{input.bin} is a EFI payload saved using either `eficheck` or `BootRomFlash.efi`
|
32
|
+
|
33
|
+
--download-signatures
|
34
|
+
|
35
|
+
Attempts to download Apple's EFI signatures from their update service
|
36
|
+
|
37
|
+
--signatures-path
|
38
|
+
|
39
|
+
Points to a path on disk where known EFI signatures exist
|
40
|
+
|
26
41
|
|
27
42
|
## Development
|
28
43
|
|
data/efivalidate.gemspec
CHANGED
@@ -5,12 +5,12 @@ require "efivalidate/version"
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "efivalidate"
|
8
|
-
spec.version =
|
8
|
+
spec.version = EFIValidate::VERSION
|
9
9
|
spec.authors = ["Rick Mark"]
|
10
|
-
spec.email = ["rickmark@
|
10
|
+
spec.email = ["rickmark@dropbox.com"]
|
11
11
|
|
12
12
|
spec.summary = %q{Validate Apple EFI images offline}
|
13
|
-
spec.description = %q{
|
13
|
+
spec.description = %q{Implements an algorithm to compare Apple EFI against their EALF baselines.}
|
14
14
|
spec.homepage = "https://github.com/rickmark/efivalidate"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
@@ -30,6 +30,9 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
31
|
spec.require_paths = ["lib"]
|
32
32
|
|
33
|
+
spec.add_runtime_dependency 'iostruct', '~> 0.0.4'
|
34
|
+
spec.add_runtime_dependency 'uuidtools', '~> 2.1'
|
35
|
+
|
33
36
|
spec.add_development_dependency "bundler", "~> 1.15"
|
34
37
|
spec.add_development_dependency "rake", "~> 10.0"
|
35
38
|
spec.add_development_dependency "rspec", "~> 3.0"
|
data/exe/efivalidate
CHANGED
@@ -1,3 +1,29 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
require 'bundler/setup'
|
2
3
|
|
3
4
|
require "efivalidate"
|
5
|
+
require 'optionparser'
|
6
|
+
|
7
|
+
if ARGV.count != 2
|
8
|
+
puts "efivalidate EFI_FILE EALF_FILE"
|
9
|
+
exit -1
|
10
|
+
end
|
11
|
+
|
12
|
+
efi = ARGV[0]
|
13
|
+
ealf = ARGV[1]
|
14
|
+
|
15
|
+
parser = EFIValidate::EALFParser.read ealf
|
16
|
+
validator = EFIValidate::EFIValidator.new parser, File.open(efi)
|
17
|
+
|
18
|
+
validator.validate
|
19
|
+
|
20
|
+
if validator.is_valid?
|
21
|
+
puts "EFI file matches EALF baseline. (#{parser.rows.count} hashes match)"
|
22
|
+
else
|
23
|
+
puts "EFI file matched #{parser.rows.count - validator.errors.count} hashes from EALF.\n"
|
24
|
+
puts "EFI file has #{validator.errors.count} hash errors:\n\n"
|
25
|
+
validator.errors.each do |error|
|
26
|
+
puts "Actual Hash: #{error.hash}"
|
27
|
+
puts "Expected Hash from EALF:\n#{error.row.to_s}\n\n"
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module EFIValidate
|
2
|
+
class EALFHeader < IOStruct.new 'a4LLC',
|
3
|
+
:ealf_magic,
|
4
|
+
:ealf_rows,
|
5
|
+
:ealf_size,
|
6
|
+
:ealf_hash_function,
|
7
|
+
:ealf_zeros,
|
8
|
+
:ealf_row_offset,
|
9
|
+
:ealf_signature_size
|
10
|
+
|
11
|
+
EALF_MAGIC = 'EALF'.freeze
|
12
|
+
|
13
|
+
EALF_ROW_SIZE = 28
|
14
|
+
|
15
|
+
EALF_HASH_FUNCTIONS = { 0 => { size: 20, function: -> { Digest::SHA1.new } }, 1 => { size: 32, funciton: -> { Digest::SHA2.new(256)} } }
|
16
|
+
|
17
|
+
EALF_HASH_SHA1 = 0
|
18
|
+
EALF_HASH_SHA256 = 1
|
19
|
+
|
20
|
+
def row_size
|
21
|
+
EALF_ROW_SIZE + self.hash_size
|
22
|
+
end
|
23
|
+
|
24
|
+
def hash_size
|
25
|
+
EFIValidate::EALFHeader::EALF_HASH_FUNCTIONS[self.ealf_hash_function][:size]
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_hash
|
29
|
+
EFIValidate::EALFHeader::EALF_HASH_FUNCTIONS[self.ealf_hash_function][:funciton].call
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module EFIValidate
|
2
|
+
class EALFParser
|
3
|
+
class EALFParseError < Exception; end
|
4
|
+
|
5
|
+
attr_reader :header, :rows
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
@header = EFIValidate::EALFHeader.read(data, 97)
|
9
|
+
|
10
|
+
raise EALFParseError, 'File header magic mismatch.' unless @header.ealf_magic == EFIValidate::EALFHeader::EALF_MAGIC
|
11
|
+
raise EALFParseError, 'Only SHA256 EALF is supported.' unless @header.ealf_hash_function == EFIValidate::EALFHeader::EALF_HASH_SHA256
|
12
|
+
|
13
|
+
@rows = []
|
14
|
+
@header.ealf_rows.times do
|
15
|
+
@rows << EFIValidate::EALFRow.read(data, @header.row_size).tap { |row| row.header = @header }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.read(file)
|
20
|
+
EFIValidate::EALFParser.new(File.open(file))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'uuidtools'
|
2
|
+
|
3
|
+
module EFIValidate
|
4
|
+
# A class that represents a region of an EFI firmware with a valid hash for the region
|
5
|
+
#
|
6
|
+
# A EALF file is a collection of regions an
|
7
|
+
class EALFRow < IOStruct.new 'CCvLLa16a*',
|
8
|
+
:ealf_component,
|
9
|
+
:ealf_region,
|
10
|
+
:ealf_master,
|
11
|
+
:ealf_offset,
|
12
|
+
:ealf_length,
|
13
|
+
:ealf_uuid,
|
14
|
+
:ealf_hash
|
15
|
+
|
16
|
+
attr_accessor :header
|
17
|
+
|
18
|
+
def hash
|
19
|
+
self.ealf_hash.each_byte.map { |b| '%02x' % b }.join
|
20
|
+
end
|
21
|
+
|
22
|
+
def uuid
|
23
|
+
UUIDTools::UUID.parse_raw(self.ealf_uuid)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
"<#{'%02x' % ealf_component}:#{ '%02x' % self.ealf_region }:#{ "%04x" % self.ealf_master }:#{ "%08x" % self.ealf_offset }:#{ "%08x" % self.ealf_length}:#{self.uuid}:#{self.hash}>"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module EFIValidate
|
2
|
+
class EFIValidator
|
3
|
+
attr_reader :parser, :data, :errors
|
4
|
+
|
5
|
+
def initialize(parser, data)
|
6
|
+
@parser = parser
|
7
|
+
@data = data
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def validate!
|
12
|
+
@errors = []
|
13
|
+
|
14
|
+
@parser.rows.each do |row|
|
15
|
+
@data.seek row.ealf_offset
|
16
|
+
|
17
|
+
section_data = @data.read row.ealf_length
|
18
|
+
|
19
|
+
calculated_hash = @parser.header.create_hash.hexdigest section_data
|
20
|
+
|
21
|
+
@errors << EFIValidationError.new(row, section_data, calculated_hash) unless calculated_hash == row.hash
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate
|
26
|
+
self.validate! unless @errors
|
27
|
+
end
|
28
|
+
|
29
|
+
def is_valid?
|
30
|
+
self.validate
|
31
|
+
|
32
|
+
self.errors.count == 0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/efivalidate/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module
|
2
|
-
VERSION = "
|
1
|
+
module EFIValidate
|
2
|
+
VERSION = "1.0.0"
|
3
3
|
end
|
data/lib/efivalidate.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
require "efivalidate/version"
|
2
|
+
require 'iostruct'
|
2
3
|
|
3
|
-
module
|
4
|
+
module EFIValidate
|
4
5
|
# Your code goes here...
|
6
|
+
autoload :EALFHeader, 'efivalidate/ealf_header'
|
7
|
+
autoload :EALFRow, 'efivalidate/ealf_row'
|
8
|
+
autoload :EALFParser, 'efivalidate/ealf_parser'
|
9
|
+
autoload :EFIValidator, 'efivalidate/efi_validator'
|
10
|
+
autoload :EFIValidationError, 'efivalidate/efi_validation_error'
|
5
11
|
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: efivalidate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rick Mark
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-12-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: iostruct
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: uuidtools
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.1'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: bundler
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,9 +80,9 @@ dependencies:
|
|
52
80
|
- - "~>"
|
53
81
|
- !ruby/object:Gem::Version
|
54
82
|
version: '3.0'
|
55
|
-
description:
|
83
|
+
description: Implements an algorithm to compare Apple EFI against their EALF baselines.
|
56
84
|
email:
|
57
|
-
- rickmark@
|
85
|
+
- rickmark@dropbox.com
|
58
86
|
executables:
|
59
87
|
- efivalidate
|
60
88
|
extensions: []
|
@@ -64,6 +92,7 @@ files:
|
|
64
92
|
- ".rspec"
|
65
93
|
- ".travis.yml"
|
66
94
|
- CODE_OF_CONDUCT.md
|
95
|
+
- FORMAT.md
|
67
96
|
- Gemfile
|
68
97
|
- LICENSE.txt
|
69
98
|
- README.md
|
@@ -73,6 +102,12 @@ files:
|
|
73
102
|
- efivalidate.gemspec
|
74
103
|
- exe/efivalidate
|
75
104
|
- lib/efivalidate.rb
|
105
|
+
- lib/efivalidate/ealf_header.rb
|
106
|
+
- lib/efivalidate/ealf_parser.rb
|
107
|
+
- lib/efivalidate/ealf_row.rb
|
108
|
+
- lib/efivalidate/efi_validation_error.rb
|
109
|
+
- lib/efivalidate/efi_validator.rb
|
110
|
+
- lib/efivalidate/intel_firmware_descriptor.rb
|
76
111
|
- lib/efivalidate/version.rb
|
77
112
|
homepage: https://github.com/rickmark/efivalidate
|
78
113
|
licenses:
|
@@ -95,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
130
|
version: '0'
|
96
131
|
requirements: []
|
97
132
|
rubyforge_project:
|
98
|
-
rubygems_version: 2.6.
|
133
|
+
rubygems_version: 2.6.14
|
99
134
|
signing_key:
|
100
135
|
specification_version: 4
|
101
136
|
summary: Validate Apple EFI images offline
|