33
44#nullable disable
55
6- using System . Buffers . Binary ;
76using System . Diagnostics ;
87using System . Reflection . PortableExecutable ;
98using System . Text . RegularExpressions ;
@@ -80,40 +79,6 @@ public void It_builds_a_runnable_apphost_by_default(string targetFramework)
8079 . HaveStdOutContaining ( "Hello World!" ) ;
8180 }
8281
83- [ PlatformSpecificTheory ( TestPlatforms . OSX ) ]
84- [ InlineData ( "netcoreapp3.1" ) ]
85- [ InlineData ( "net5.0" ) ]
86- [ InlineData ( ToolsetInfo . CurrentTargetFramework ) ]
87- public void It_can_disable_codesign_if_opt_out ( string targetFramework )
88- {
89- var testAsset = _testAssetsManager
90- . CopyTestAsset ( "HelloWorld" , identifier : targetFramework )
91- . WithSource ( )
92- . WithTargetFramework ( targetFramework ) ;
93-
94- var buildCommand = new BuildCommand ( testAsset ) ;
95- buildCommand
96- . Execute ( new string [ ] {
97- "/p:_EnableMacOSCodeSign=false;ProduceReferenceAssembly=false" ,
98- } )
99- . Should ( )
100- . Pass ( ) ;
101-
102- var outputDirectory = buildCommand . GetOutputDirectory ( targetFramework ) ;
103- var appHostFullPath = Path . Combine ( outputDirectory . FullName , "HelloWorld" ) ;
104-
105- // Check that the apphost was not signed
106- var codesignPath = @"/usr/bin/codesign" ;
107- new RunExeCommand ( Log , codesignPath , new string [ ] { "-d" , appHostFullPath } )
108- . Execute ( )
109- . Should ( )
110- . Fail ( )
111- . And
112- . HaveStdErrContaining ( $ "{ appHostFullPath } : code object is not signed at all") ;
113-
114- outputDirectory . Should ( ) . OnlyHaveFiles ( GetExpectedFilesFromBuild ( testAsset , targetFramework ) ) ;
115- }
116-
11782 [ PlatformSpecificTheory ( TestPlatforms . OSX ) ]
11883 [ InlineData ( "netcoreapp3.1" , "win-x64" ) ]
11984 [ InlineData ( "net5.0" , "win-x64" ) ]
@@ -154,20 +119,35 @@ public void It_does_not_try_to_codesign_non_osx_app_hosts(string targetFramework
154119 }
155120
156121 [ Theory ]
157- [ InlineData ( "net6.0" , "osx-x64" ) ]
158- [ InlineData ( "net6.0" , "osx-arm64" ) ]
159- [ InlineData ( ToolsetInfo . CurrentTargetFramework , "osx-x64" ) ]
160- [ InlineData ( ToolsetInfo . CurrentTargetFramework , "osx-arm64" ) ]
161- public void It_codesigns_an_app_targeting_osx ( string targetFramework , string rid )
122+ [ InlineData ( "net8.0" , "osx-x64" , true ) ]
123+ [ InlineData ( "net8.0" , "osx-arm64" , true ) ]
124+ [ InlineData ( ToolsetInfo . CurrentTargetFramework , "osx-x64" , true ) ]
125+ [ InlineData ( ToolsetInfo . CurrentTargetFramework , "osx-arm64" , true ) ]
126+ [ InlineData ( "net8.0" , "osx-x64" , false ) ]
127+ [ InlineData ( "net8.0" , "osx-arm64" , false ) ]
128+ [ InlineData ( ToolsetInfo . CurrentTargetFramework , "osx-x64" , false ) ]
129+ [ InlineData ( ToolsetInfo . CurrentTargetFramework , "osx-arm64" , false ) ]
130+ [ InlineData ( "net8.0" , "osx-x64" , null ) ]
131+ [ InlineData ( "net8.0" , "osx-arm64" , null ) ]
132+ [ InlineData ( ToolsetInfo . CurrentTargetFramework , "osx-x64" , null ) ]
133+ [ InlineData ( ToolsetInfo . CurrentTargetFramework , "osx-arm64" , null ) ]
134+ public void It_codesigns_an_app_targeting_osx ( string targetFramework , string rid , bool ? enableMacOSCodesign )
162135 {
136+ const bool CodesignsByDefault = true ;
163137 const string testAssetName = "HelloWorld" ;
164138 var testAsset = _testAssetsManager
165139 . CopyTestAsset ( testAssetName , identifier : targetFramework )
166140 . WithSource ( )
167141 . WithTargetFramework ( targetFramework ) ;
168142
169143 var buildCommand = new BuildCommand ( testAsset ) ;
144+
170145 var buildArgs = new List < string > ( ) { $ "/p:RuntimeIdentifier={ rid } " } ;
146+ if ( enableMacOSCodesign . HasValue )
147+ {
148+ buildArgs . Add ( $ "/p:_EnableMacOSCodeSign={ enableMacOSCodesign . Value } ") ;
149+ }
150+
171151 buildCommand
172152 . Execute ( buildArgs . ToArray ( ) )
173153 . Should ( )
@@ -176,22 +156,14 @@ public void It_codesigns_an_app_targeting_osx(string targetFramework, string rid
176156 var outputDirectory = buildCommand . GetOutputDirectory ( targetFramework : targetFramework , runtimeIdentifier : rid ) ;
177157 var appHostFullPath = Path . Combine ( outputDirectory . FullName , testAssetName ) ;
178158
179- // Check that the apphost is signed
180- HasMachOSignatureLoadCommand ( new FileInfo ( appHostFullPath ) ) . Should ( ) . BeTrue ( ) ;
181- // When on a Mac, use the codesign tool to verify the signature as well
159+ // Check that the apphost is signed if expected
160+ var shouldBeSigned = enableMacOSCodesign ?? CodesignsByDefault ;
161+ MachOSignature . HasMachOSignatureLoadCommand ( new FileInfo ( appHostFullPath ) ) . Should ( ) . Be ( shouldBeSigned , $ "The app host should { ( shouldBeSigned ? "" : "not " ) } have a Mach-O signature load command." ) ;
182162 if ( RuntimeInformation . IsOSPlatform ( OSPlatform . OSX ) )
183163 {
184- var codesignPath = @"/usr/bin/codesign" ;
185- new RunExeCommand ( Log , codesignPath , [ "-s" , "-" , appHostFullPath ] )
186- . Execute ( )
187- . Should ( )
188- . Fail ( )
189- . And
190- . HaveStdErrContaining ( $ "{ appHostFullPath } : is already signed") ;
191- new RunExeCommand ( Log , codesignPath , [ "-v" , appHostFullPath ] )
192- . Execute ( )
164+ MachOSignature . HasValidMachOSignature ( new FileInfo ( appHostFullPath ) , Log )
193165 . Should ( )
194- . Pass ( ) ;
166+ . Be ( shouldBeSigned , $ "The app host should have a valid Mach-O signature for { rid } ." ) ;
195167 }
196168 }
197169
@@ -487,62 +459,5 @@ private static bool IsPE32(string path)
487459 return reader . PEHeaders . PEHeader . Magic == PEMagic . PE32 ;
488460 }
489461 }
490-
491- // Reads the Mach-O load commands and returns true if an LC_CODE_SIGNATURE command is found, otherwise returns false
492- static bool HasMachOSignatureLoadCommand ( FileInfo file )
493- {
494- /* Mach-O files have the following structure:
495- * 32 byte header beginning with a magic number and info about the file and load commands
496- * A series of load commands with the following structure:
497- * - 4-byte command type
498- * - 4-byte command size
499- * - variable length command-specific data
500- */
501- const uint LC_CODE_SIGNATURE = 0x1D ;
502- using ( var stream = file . OpenRead ( ) )
503- {
504- // Read the MachO magic number to determine endianness
505- Span < byte > eightByteBuffer = stackalloc byte [ 8 ] ;
506- stream . ReadExactly ( eightByteBuffer ) ;
507- // Determine if the magic number is in the same or opposite endianness as the runtime
508- bool reverseEndinanness = BitConverter . ToUInt32 ( eightByteBuffer . Slice ( 0 , 4 ) ) switch
509- {
510- 0xFEEDFACF => false ,
511- 0xCFFAEDFE => true ,
512- _ => throw new InvalidOperationException ( "Not a 64-bit Mach-O file" )
513- } ;
514- // 4-byte value at offset 16 is the number of load commands
515- // 4-byte value at offset 20 is the size of the load commands
516- stream . Position = 16 ;
517- ReadUInts ( stream , eightByteBuffer , out uint loadCommandsCount , out uint loadCommandsSize ) ;
518- // Mach-0 64 byte headers are 32 bytes long, and the first load command will be right after
519- stream . Position = 32 ;
520- bool hasSignature = false ;
521- for ( int commandIndex = 0 ; commandIndex < loadCommandsCount ; commandIndex ++ )
522- {
523- ReadUInts ( stream , eightByteBuffer , out uint commandType , out uint commandSize ) ;
524- if ( commandType == LC_CODE_SIGNATURE )
525- {
526- hasSignature = true ;
527- }
528- stream . Position += commandSize - eightByteBuffer . Length ;
529- }
530- Debug . Assert ( stream . Position == loadCommandsSize + 32 ) ;
531- return hasSignature ;
532-
533- void ReadUInts ( Stream stream , Span < byte > buffer , out uint val1 , out uint val2 )
534- {
535- stream . ReadExactly ( buffer ) ;
536- val1 = BitConverter . ToUInt32 ( buffer . Slice ( 0 , 4 ) ) ;
537- val2 = BitConverter . ToUInt32 ( buffer . Slice ( 4 , 4 ) ) ;
538- if ( reverseEndinanness )
539- {
540- val1 = BinaryPrimitives . ReverseEndianness ( val1 ) ;
541- val2 = BinaryPrimitives . ReverseEndianness ( val2 ) ;
542- }
543- }
544- }
545- }
546-
547462 }
548463}
0 commit comments