/
IoTTestCommands.ps1
413 lines (358 loc) · 12.9 KB
/
IoTTestCommands.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
<#
This module contains the Test functions to validate the signatures of the binaries
#>
. $PSScriptRoot\IoTPrivateFunctions.ps1
#string array to cache the CrossCAList
[string[]] $CrossCAList
function Get-IoTCrossCAList() {
if (!$Script:CrossCAList) {
$ConfigFile = "$PSScriptRoot\IoTEnvSettings.xml"
[xml] $Config = Get-Content -Path $ConfigFile
$Script:CrossCAList = $Config.IoTEnvironment.CrossCertCAs.CrossCertCA
}
$Script:CrossCAList
}
function Test-IoTCabSignature {
<#
.SYNOPSIS
Checks if the cab file and its contents are properly signed.
.DESCRIPTION
Checks if the cab file and its contents are properly signed.
.PARAMETER CabFile
Mandatory parameter, the cab file to be inspected
.PARAMETER Config
Mandatory parameter, specifing the Config. Can be "Retail" or any other ("Test"/"Dev" etc)
.INPUTS
None
.OUTPUTS
System.Boolean
True if the cab file is signed properly.
.EXAMPLE
$result = Test-IoTCabSignature C:\myfile.cab Retail
.NOTES
Uses Test-IoTSignature cmdlet to validate the .cab file, and its contents - .cat. .exe, .dll and .sys
.LINK
[Test-IoTSignature](Test-IoTSignature.md)
#>
[CmdletBinding()]
[OutputType([Boolean])]
Param
(
# Product name to process
[Parameter(Position = 0, Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]$CabFile,
# Product Configuration to process
[Parameter(Position = 1, Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]$Config
)
$retval = $true;
if (!(Test-Path $CabFile)) {
Publish-Error "$CabFile not found."
return $false
}
if ((Get-Item $CabFile).Extension -ine ".cab") {
Publish-Error "$CabFile is not supported. Provide a .cab file"
return $false
}
# First check the signature of the cab itself
$retval = Test-IoTSignature $CabFile $Config
$files_to_check = @("*.dll", "*.sys", "*.exe", "*.cat")
# Expand cab to get required binaries to a temp dir
cmd /r "expand $CabFile $env:TMP -F:*" | Out-Null
$cabcontents = Get-ChildItem -Path $env:TMP -Recurse -Include $files_to_check | Foreach-Object {$_.FullName}
# Check signature of the binaries
if ($null -ne $cabcontents) {
foreach ($file in $cabcontents) {
$result = Test-IoTSignature $file $Config
if (!$result) { $retval = $false }
}
}
else { Write-Debug "No $files_to_check in $CabFile" }
Clear-Temp
return $retval
}
function Test-IoTSignature {
<#
.SYNOPSIS
Checks if the file is properly signed.
.DESCRIPTION
Checks if the file is properly signed. For Retail Config, it checks if the signature is rooted to Microsoft or a cross rooted cert.
.PARAMETER FileName
Mandatory parameter, the file to be inspected
.PARAMETER Config
Mandatory parameter, specifying the Config. Can be "Retail" or any other ("Test"/"Dev" etc)
.INPUTS
None
.OUTPUTS
System.Boolean
True if the file is properly signed.
.EXAMPLE
$result = Test-IoTSignature C:\myfile.dll Retail
.NOTES
This verifies using the signtool. [ signtool verify /v /pa FileName ]
.LINK
[Test-IoTCabSignature](Test-IoTCabSignature.md)
#>
[CmdletBinding()]
[OutputType([Boolean])]
Param
(
# Product name to process
[Parameter(Position = 0, Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]$FileName,
# Product Configuration to process
[Parameter(Position = 1, Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]$Config
)
$retval = $true
$Name = Split-Path -Path $FileName -Leaf
$signresult = cmd /r signtool verify /v /pa $FileName
if ($lastExitCode) {
Publish-Error "$Name is not signed"
return $false
}
if ( ($signresult | ForEach-Object { $_.Contains("(TEST ONLY)") }) -contains $true ) {
# Warn about test signature only for retail Config. Otherwise stay quiet.
if ($Config -ieq "Retail") {
Publish-Warning " $Name is test signed"
$retval = $false
}
}
else {
foreach ($line in $signresult) {
#TODO: Check for OEM UMCI. Currently only checking cross certs for drivers
if ($line.contains(" Issued by")) {
$crosscalist = Get-IoTCrossCAList
$ca = ($line.Split(":"))[1].trim()
if ($crosscalist.contains($ca)) {
# Looks good. The text message suppressed. Warn only when there is a concern.
Write-Verbose "$Name signed with $ca issued Cross Cert"
}
else {
if (!$ca.contains("Microsoft")) {
# Not signed by Microsoft or cross-cert, so warn.
Publish-Warning " $Name not signed with a cross signed cert. Signed with $ca"
$retval = $false
}
else {
# Looks good. The text message suppressed. Warn only when there is a concern.
Write-Verbose "$Name signed with $ca Cert"
}
}
break
}
}
}
return $retval
}
function Test-IoTCerts {
<#
.SYNOPSIS
Checks if the certs in the workspace folder are all valid.
.DESCRIPTION
Checks if the certs in the workspace folder are all valid.
.INPUTS
None
.OUTPUTS
System.Boolean
True if the file is properly signed.
.EXAMPLE
$result = Test-IoTCerts
.NOTES
This verifies using the Test-Certificate.
.LINK
[Test-Certificate](https://docs.microsoft.com/powershell/module/pkiclient/test-certificate?view=win10-ps)
#>
[CmdletBinding()]
[OutputType([Boolean])]
Param
(
)
$retval = $true
$certs = Get-ChildItem -Path $env:SRC_DIR, $env:COMMON_DIR -File -Filter *.cer -Recurse | Foreach-Object {$_.FullName}
if ($null -eq $certs) {
Publish-Status "No certs found."
}
$certs = @($certs)
foreach ($cert in $certs) {
# X509Certificate2 object that will represent the certificate
$certobj = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
# Imports the certificate from file to x509Certificate object
$certobj.Import($cert)
$ret = Test-Certificate $certobj -AllowUntrustedRoot
if (!$ret) {
$retval = $false
Publish-Error "$cert is invalid"
}
}
return $retval
}
function Add-IoTSignature {
<#
.SYNOPSIS
Signs the files with the certificate selected via Set-IoTSignature
.DESCRIPTION
Signs the files with the certificate selected via Set-IoTSignature
.PARAMETER Path
Path for the files to be signed. Can be a single file or a directory of files
.PARAMETER Type
Optional parameter indicating the file type(s) to be signed. If omitted, all file types (*.exe,*.dll.*.sys,*.cat) will be signed.
.EXAMPLE
Add-IoTSignature C:\QCSBSP
.EXAMPLE
Add-IoTSignature C:\QCSBSP *.sys,*.dll
.NOTES
.LINK
[Test-IoTSignature](Test-IoTSignature.md)
#>
[CmdletBinding()]
Param
(
[Parameter(Position = 0, Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]$Path,
[Parameter(Position = 1, Mandatory = $false)]
[String[]]$Type = $null
)
$types = @('*.exe', '*.sys', '*.dll', '*.cat')
if (Test-Path $Path -PathType Container) {
$filter = $types
if ($Type) {
$check = $Type | Where-Object { $types -notcontains $_ }
if ($check ) {
Publish-Error "$check not supported"
return
}
$filter = $Type
}
$filestosign = (Get-ChildItem $Path -File -Include $filter -Recurse) | ForEach-Object {$_.FullName}
if (!$filestosign) {
Publish-Warning "No $($filter -join ",") files to sign in $Path"
return
}
foreach ($file in $filestosign) {
Write-Verbose "Signing $file"
sign $file | Out-Null
}
}
elseif (Test-Path $Path -PathType Leaf -Include $types) {
#Single file. Sign this file
Write-Verbose "Signing $file"
sign $Path | Out-Null
}
else {
Publish-Error "$Path cannot be signed"
}
}
function Redo-IoTCabSignature {
<#
.SYNOPSIS
Resigns the cab file and its contents / cat files with the certificate set in the environment.
.DESCRIPTION
Resigns the cab file and its contents / cat files with the certificate set in the environment. This is useful to sign the cabs from Silicon Vendors with OEM Cross certificate.
.PARAMETER Path
Path containing the source cab files
.PARAMETER DestinationPath
Path where the resigned cabs are stored.
.EXAMPLE
Redo-IoTCabSignature C:\QCSBSP C:\QCBSP-Signed
.NOTES
.LINK
[Test-IoTSignature](Test-IoTSignature.md)
#>
[CmdletBinding()]
Param
(
[Parameter(Position = 0, Mandatory = $true)]
[ValidateScript( { Test-Path $_ -PathType Container })]
[String]$Path,
# Product Configuration to process
[Parameter(Position = 1, Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]$DestinationPath
)
New-DirIfNotExist $DestinationPath
$excludesign = ".HalExt"
$cabfiles = Get-ChildItem -Path $Path -Filter *.cab
if (!$cabfiles) {
Publish-Error "No .cab files found in $Path"
return
}
foreach ($cabfile in $cabfiles) {
$filename = $cabfile.BaseName
Publish-Status "Processing $filename.cab"
$tempdir = "$DestinationPath\$filename"
New-Item -Path $tempdir -ItemType Container | Out-Null
Write-Debug "pkgsigntool unpack : $tempdir"
pkgsigntool unpack $cabfile.FullName /out:$tempdir | Out-Null
if ($filename.Contains($excludesign)) {
Write-Verbose "Skipping signing for $filename ($excludesign)"
}
else {
Add-IoTSignature $tempdir *.sys, *.dll
}
Write-Debug "pkgsigntool update : $tempdir"
pkgsigntool update $tempdir | Out-Null
Write-Debug "makecat : $tempdir\content.cdf"
makecat $tempdir\content.cdf | Out-Null
Write-Debug "signcat : $tempdir\update.cat"
sign $tempdir\update.cat | Out-Null
Write-Debug "pkgsigntool repack : $tempdir.cab"
pkgsigntool repack $tempdir /out:"$tempdir.cab" | Out-Null
Write-Debug "signcab : $tempdir.cab"
sign "$tempdir.cab" | Out-Null
Remove-Item -Path $tempdir -Recurse -Force | Out-Null
}
}
function Test-IoTSignatureNew([string] $filename, [string] $Config) {
$retval = $false
$Name = Split-Path -Path $filename -Leaf
# Get a X590Certificate2 certificate object for a file
$authenticode = Get-AuthenticodeSignature -FilePath $filename
if ($authenticode.Status -ine "Valid") {
Publish-Error "$Name signature status is $($authenticode.Status)"
}
else {
$cert = $authenticode.SignerCertificate
$issuerht = $cert.IssuerName.Name
$issuername = $issuerht.Split(",")[0].Replace("CN=", "")
Write-Debug "File is signed by cert issued by $issuername"
if ($issuername.contains("Microsoft")) {
Write-Verbose "$Name signed with $issuername Cert"
$retval = $true
}
elseif ($issuername.contains("TEST ONLY")) {
if ($Config -ine "Retail") {
Write-Verbose "$Name signed with test cert :$issuername "
$retval = $true
}
else { Publish-Error "$Name is test signed" }
}
else {
# Create a new chain to store the certificate chain
$chain = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Chain
# Build the certificate chain from the file certificate
if ($chain.Build($cert)) {
$rootcert = $chain.ChainElements[$chain.ChainElements.Count - 1].Certificate
$rootht = $rootcert.IssuerName.Name
$rootca = $rootht.Split(",")[0].Replace("CN=", "")
$crosscalist = Get-IoTCrossCAList
if ($crosscalist.contains($rootca)) {
# Looks good. The text message suppressed. Warn only when there is a concern.
Write-Verbose "$Name signed with $rootca issued Cross Cert"
$retval = $true
}
else {
# Not signed by Microsoft or cross-cert, so warn.
Publish-Warning " $Name not signed with a cross signed cert. Signed with $rootca"
}
}
else { Publish-Error "Trust chain cannot be established." }
}
}
return $retval
}