@@ -23,6 +23,7 @@ but with additional functionality, improved developer experience and performance
2323 - [ 3.10. PackageVolume Repair] ( #310-packagevolume-repair )
2424 - [ 3.11. Usability] ( #311-usability )
2525 - [ 3.12. Is\* Provisioned()] ( #312-312-isprovisioned )
26+ - [ 3.13. PackageValidation] ( #313-packagevalidation )
2627- [ 4. Examples] ( #4-examples )
2728 - [ 4.1. AddPackageAsync()] ( #41-addpackageasync )
2829 - [ 4.2. AddPackageByUriAsync()] ( #42-addpackagebyuriasync )
@@ -73,6 +74,7 @@ Additional functionality includes:
7374* IsPackageRegistrationPending -- Is there an update waiting to register?
7475* PackageSets -- Batch operations
7576* PackageRuntimeManager -- Batch operations for use at runtime via Dynamic Dependencies
77+ * PackageValidation -- Validate a package has expected identity, signature, etc. before adding/staging
7678* Usability -- Quality-of-Life enhancements
7779
7880## 3.1. API Structure
@@ -398,6 +400,30 @@ Is\*Provisioned\*() methods determine if the target is provisioned.
398400
399401These methods require administrative privileges.
400402
403+ ## 3.13. PackageValidation
404+
405+ This API allows callers to verify that packages being processed by Add* , Ensure* , and Stage* APIs
406+ of PackageDeploymentManager match what are expected from their URI.
407+
408+ When adding or staging a package from an external source such as HTTP URI or uncontrolled file
409+ location, a malicious actor might perform a man-in-the-middle attack to intercept and tamper with
410+ the package data being read, causing a malicious package to be installed instead of the expected
411+ one. The package might also be tampered at the source through supply-chain attacks. Verifying
412+ the identity and signature of target packages helps ensure that such attacks have not happened.
413+
414+ The following runtimeclasses implement ` PackageValidation ` handlers, and are available for use
415+ directly:
416+ * ` PackageFamilyNameValidator ` : Validates that the package has the expected package family name.
417+ * ` PackageMinimumVersionValidator ` : Validates that the package has at least the expected minimum
418+ version number.
419+ * ` PackageCertificateEkuValidator ` : Validates that the certificate used to sign the package
420+ contains the expected Extended Key Usage (EKU) value.
421+
422+ Custom validators can be implemented as handlers for the
423+ ` PackageValidationEventSource.ValidationRequested ` event. These can verify any part of packages'
424+ [ footprint data] ( https://learn.microsoft.com/windows/win32/api/appxpackaging/ne-appxpackaging-appx_bundle_footprint_file_type )
425+ (manifest, block map, and digital signature).
426+
401427# 4. Examples
402428
403429## 4.1. AddPackageAsync()
@@ -761,6 +787,136 @@ PackageVersion ToVersion(uint major, uint minor, uint build, uint revision) =>
761787 };
762788```
763789
790+ ## 4.9. PackageValidation
791+
792+ ### 4.9.1. Using built-in PackageValidation handlers
793+
794+ This example shows how to use built - in PackageValidation handlers to verify
795+ package family name , minimum version , and certificate EKU .
796+
797+ ```c #
798+ using Microsoft .Windows .Management .Deployment ;
799+
800+ var pdm = PackageDeploymentManager ().GetDefault ();
801+ var packageUri = new Uri (" https://contoso.com/package.msix" );
802+
803+ var options = new AddPackageOptions ();
804+ var validators = options .GetValidationEventSourceForUri (packageUri ).ValidationRequested ;
805+ validators += new PackageFamilyNameValidator (" ExpectedFamilyName_1234567890abc" ).Handler ;
806+ validators += new PackageMinimumVersionValidator (new Windows .ApplicationModel .PackageVersion (2 , 0 , 0 , 0 )).Handler ;
807+ validators += new PackageCertificateEkuValidator (" 1.3.6.1.4.1.311.2.1.11" ).Handler ;
808+
809+ var deploymentResult = await pdm .AddPackageAsync (packageUri , options );
810+ if (deploymentResult .Status == PackageDeploymentStatus .CompletedSuccess )
811+ {
812+ Console .WriteLine (" Success" );
813+ }
814+ else // deploymentResult.Status == PackageDeploymentStatus.CompletedFailure
815+ {
816+ var error = deploymentResult .Error .HResult ;
817+ if (error = 0x 80080219 /* APPX_E_DIGEST_MISMATCH*/ )
818+ {
819+ Console .WriteLine (" The package retrieved from the specified URI isn't expected according to PackageValidators" );
820+ }
821+ else
822+ {
823+ var extendedError = deploymentResult .ExtendedError .HResult ;
824+ var message = deploymentResult .MessageText ;
825+ Console .WriteLine ($" An error occurred while adding the package. Error 0x{error : X08 } ExtendedError 0x{extendedError : X08 } {message }" );
826+ }
827+ }
828+ ```
829+
830+ ### 4.9.2. Using custom PackageValidation handlers
831+
832+ This example shows how to implement a custom PackageValidation handler , and use it to verify a package .
833+
834+ ```c #
835+ using Microsoft .Windows .Management .Deployment ;
836+
837+ // Consuming COM APIs for IAppxPackageReader, IAppxManifestReader, etc. requires C# interop definitions.
838+ // Assume standard interop definitions exist for relevant APIs in AppxPackaging.h.
839+ [Guid (" b5c49650-99bc-481c-9a34-3d53a4106708" ), InterfaceType (ComInterfaceType .InterfaceIsIUnknown )]
840+ public interface IAppxPackageReader { .. . }
841+
842+ // Implementation of the custom package validation handler
843+ bool IsPackageValid (object package )
844+ {
845+ var packageReader = package as IAppxPackageReader ;
846+ if (packageReader == null )
847+ {
848+ // object is not an msix package as expected (i.e. it is a bundle), reject it
849+ return false ;
850+ }
851+
852+ IAppxManifestReader manifestReader ;
853+ packageReader .GetManifest (out manifestReader );
854+
855+ var manifestReader3 = manifestReader as IAppxManifestReader3 ;
856+ IAppxManifestCapabilitiesEnumerator capabilitiesEnumerator ;
857+ manifestReader3 .GetCapabilitiesByCapabilityClass (APPX_CAPABILITY_CLASS_ALL , out capabilitiesEnumerator );
858+
859+ bool hasCapabilities ;
860+ capabilitiesEnumerator .GetHasCurrent (out hasCapabilities );
861+ return ! hasCapabilities ;
862+ }
863+
864+ void MyPackageValidationHandler (object sender , PackageValidationEventArgs args )
865+ {
866+ var deferral = args .GetDeferral ();
867+
868+ bool isValid = false ;
869+ try
870+ {
871+ isValid = IsPackageValid (args .Package );
872+ }
873+ finally
874+ {
875+ if (isValid )
876+ {
877+ Log (" Package at URI is valid: " + args .PackageUri );
878+ }
879+ else
880+ {
881+ Log (" Package at URI is not valid: " + args .PackageUri );
882+ args .Cancel = true ;
883+ }
884+
885+ deferral .Complete ();
886+ }
887+ }
888+
889+ // Code that utilizes the custom package validator
890+ void InstallPackageWithCustomValidation ()
891+ {
892+ var pdm = PackageDeploymentManager ().GetDefault ();
893+ var packageUri = new Uri (" https://contoso.com/package.msix" );
894+
895+ var options = new AddPackageOptions ();
896+ options .GetValidationEventSourceForUri (packageUri ).ValidationRequested += MyPackageValidationHandler ;
897+
898+ var deploymentResult = await pdm .AddPackageAsync (packageUri , options );
899+ if (deploymentResult .Status == PackageDeploymentStatus .CompletedSuccess )
900+ {
901+ Console .WriteLine (" Success" );
902+ }
903+ else // deploymentResult.Status == PackageDeploymentStatus.CompletedFailure
904+ {
905+ var error = deploymentResult .Error .HResult ;
906+ if (error = 0x 80080219 /* APPX_E_DIGEST_MISMATCH*/ )
907+ {
908+ Console .WriteLine (" The package retrieved from the specified URI isn't expected according to PackageValidators" );
909+ }
910+ else
911+ {
912+ var extendedError = deploymentResult .ExtendedError .HResult ;
913+ var message = deploymentResult .MessageText ;
914+ Console .WriteLine ($" An error occurred while adding the package. Error 0x{error : X08 } ExtendedError 0x{extendedError : X08 } {message }" );
915+ }
916+ }
917+ }
918+ ```
919+
764920# 5. Remarks
765921
766922## 5.1. Platform Support
@@ -857,6 +1013,43 @@ namespace Microsoft.Windows.Management.Deployment
8571013 NewerAvailable = 2 ,
8581014 };
8591015
1016+ [contract (PackageDeploymentContract , 3 )]
1017+ runtimeclass PackageValidationEventArgs
1018+ {
1019+ Windows .Foundation .Uri PackageUri { get ; };
1020+ IInspectable AppxPackagingObject { get ; };
1021+ Boolean Cancel ;
1022+
1023+ Windows .Foundation .Deferral GetDeferral ();
1024+ }
1025+
1026+ [contract (PackageDeploymentContract , 3 )]
1027+ runtimeclass PackageValidationEventSource
1028+ {
1029+ event Windows .Foundation .TypedEventHandler < PackageValidationEventSource , PackageValidationEventArgs > ValidationRequested ;
1030+ }
1031+
1032+ [contract (PackageDeploymentContract , 3 )]
1033+ runtimeclass PackageFamilyNameValidator
1034+ {
1035+ PackageFamilyNameValidator (String packageUri , String expectedPackageFamilyName );
1036+ Windows .Foundation .TypedEventHandler < PackageValidationEventSource , PackageValidationEventArgs > Handler { get ; };
1037+ }
1038+
1039+ [contract (PackageDeploymentContract , 3 )]
1040+ runtimeclass PackageMinimumVersionValidator
1041+ {
1042+ PackageMinimumVersionValidator (Windows .ApplicationModel .PackageVersion minimumVersion );
1043+ Windows .Foundation .TypedEventHandler < PackageValidationEventSource , PackageValidationEventArgs > Handler { get ; };
1044+ }
1045+
1046+ [contract (PackageDeploymentContract , 3 )]
1047+ runtimeclass PackageCertificateEkuValidator
1048+ {
1049+ PackageCertificateEkuValidator (String expectedCertificateEku );
1050+ Windows .Foundation .TypedEventHandler < PackageValidationEventSource , PackageValidationEventArgs > Handler { get ; };
1051+ }
1052+
8601053 /// The progress status of the deployment request.
8611054 /// @see https://learn.microsoft.com/uwp/api/windows.management.deployment.deploymentprogress.state
8621055 [contract (PackageDeploymentContract , 1 )]
@@ -964,6 +1157,15 @@ namespace Microsoft.Windows.Management.Deployment
9641157
9651158 Boolean IsLimitToExistingPackagesSupported { get ; }; // Requires Windows >= 10.0.22621.0 (aka Win11 22H2)
9661159 Boolean LimitToExistingPackages ;
1160+
1161+ [contract (PackageDeploymentContract , 3 )]
1162+ Boolean IsPackageValidationSupported { get ; };
1163+
1164+ [contract (PackageDeploymentContract , 3 )]
1165+ IMapView < Windows .Foundation .Uri , PackageValidationEventSource > PackageValidators { get ; };
1166+
1167+ [contract (PackageDeploymentContract , 3 )]
1168+ PackageValidationEventSource GetValidationEventSourceForUri (Windows .Foundation .Uri uri );
9671169 }
9681170
9691171 // Requires Windows >= 10.0.19041.0 (aka 2004 aka 20H1)
@@ -988,6 +1190,15 @@ namespace Microsoft.Windows.Management.Deployment
9881190
9891191 Boolean IsExpectedDigestsSupported { get ; }; // Requires Windows >= 10.0.22621.0 (aka Win11 22H2)
9901192 IMap < Windows .Foundation .Uri , String > ExpectedDigests { get ; };
1193+
1194+ [contract (PackageDeploymentContract , 3 )]
1195+ Boolean IsPackageValidationSupported { get ; };
1196+
1197+ [contract (PackageDeploymentContract , 3 )]
1198+ IMapView < Windows .Foundation .Uri , PackageValidationEventSource > PackageValidators { get ; };
1199+
1200+ [contract (PackageDeploymentContract , 3 )]
1201+ PackageValidationEventSource GetValidationEventSourceForUri (Windows .Foundation .Uri uri );
9911202 }
9921203
9931204 // Requires Windows >= 10.0.19041.0 (aka 2004 aka 20H1)
0 commit comments