Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
ba4efe9
feat: introduce sign mode for driver packaging, allowing skipping of …
svasista-ms Apr 27, 2026
c3a080e
refactor: remove Authenticode signature checks from sign mode off tests
svasista-ms Apr 27, 2026
490f323
test: add verification skip test for sign mode off with signature ver…
svasista-ms Apr 27, 2026
a13c7e3
Merge branch 'main' into sign-mode-off
svasista-ms May 6, 2026
1c1374a
refactor: sign mode variant names to lowercase and clean up related t…
svasista-ms May 7, 2026
d7a17ae
refactor(tests): remove build help test
svasista-ms May 12, 2026
37b8d2d
refactor(package_task): enforce signature verification rules and upda…
svasista-ms May 12, 2026
13e2280
Merge branch 'microsoft:main' into sign-mode-off
svasista-ms May 13, 2026
b32d8f9
refactor: move SignMode enum to cli layer and introduce SignConfig fo…
svasista-ms May 18, 2026
317e2d3
refactor: update documentation for SignConfig and SignMode enums
svasista-ms May 18, 2026
844306e
refactor(package_task): simplify sign_and_verify method documentation
svasista-ms May 18, 2026
e4c0622
refactor(main): update main function to return ExitCode and improve e…
svasista-ms May 19, 2026
3edac63
refactor(README): add --sign-mode option to build command and update …
svasista-ms May 19, 2026
148356e
refactor: rename SignConfig to SignMode
svasista-ms May 19, 2026
d5b0ee1
refactor(README): usage of --sign-mode flag in build command
svasista-ms May 21, 2026
55ca56f
refactor(package_task): simplify documentation for SignMode Off and u…
svasista-ms May 21, 2026
d793ec5
refactor: improve tests and documentation for --sign-mode flag
svasista-ms May 22, 2026
d536fd8
refactor(README): update build command sign-mode example
svasista-ms May 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions crates/cargo-wdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Usage: cargo wdk build [OPTIONS]
Options:
--profile <PROFILE> Build artifacts with the specified profile
--target-arch <TARGET_ARCH> Build for the target architecture
--sign-mode <SIGN_MODE> Driver signing mode [default: test] [possible values: off, test]
--verify-signature Verify the signature
--sample Build sample class driver project
-h, --help Print help
Expand All @@ -74,7 +75,7 @@ Verbosity:
-q, --quiet... Decrease logging verbosity
```

`build` takes a number of inputs specifying build profile (`dev` or `release`), target architecture (`amd64` or `arm64`), a flag enabling signature verification and a flag indicating a sample driver along with verbosity flags.
`build` takes a number of inputs specifying build profile (`dev` or `release`), target architecture (`amd64` or `arm64`), the driver signing mode, a flag enabling signature verification and a flag indicating a sample driver along with verbosity flags.

When the command completes the packaged driver artifacts are emitted at the path `target\<profile>\<project-name>-package`.

Expand All @@ -90,9 +91,14 @@ If you have a workspace with a mix of sample and non-sample driver projects, the

#### Signing and Verification

To sign driver artifacts `build` looks for a certificate called `WDRLocalTestCert` in a store called `WDRTestCertStore`. Make sure you place your signing certificate there with that name. If no certificate is found, `build` will automatically generate a new self-signed one and add it for you.
The `build` command has a `--sign-mode` flag that controls how driver artifacts are signed. It accepts the following values:

If the `--verify-signature` flag is provided, the signatures are verified after signing. For verification to work, make sure you add a copy of the signing certificate in the `Trusted Root Certification Authorities` store. For security reasons `build` does not automatically do this even when it automatically generates the cert. You will have to always perform this step manually.
- `test` (default): Sign with a test certificate. The command looks for a certificate called `WDRLocalTestCert` in a store called `WDRTestCertStore`. If you wish to use your own certificate, add it to the same store with the same name. Otherwise a self-signed certificate will be automatically generated, added, and used for signing.
- `off`: Skip signing entirely. This is useful when you intend to sign the artifacts later with your own toolchain.

If the `--verify-signature` flag is provided, the signatures are verified after signing. For verification to work, make sure you add a copy of the signing certificate in the `Trusted Root Certification Authorities` store. For security reasons `build` does not automatically do this even when it automatically generates the cert. You will have to always perform this step manually.

`--verify-signature` cannot be combined with `--sign-mode=off` because if signing is off there is nothing to verify. Passing both will cause `build` to fail with an error.

#### Examples

Expand All @@ -113,3 +119,9 @@ If the `--verify-signature` flag is provided, the signatures are verified after
```pwsh
cargo wdk build --target-arch amd64
```

- To build a driver project with signing off, navigate to the root of the project and run:

```pwsh
cargo wdk build --sign-mode off
```
9 changes: 5 additions & 4 deletions crates/cargo-wdk/src/actions/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use build_task::BuildTask;
use cargo_metadata::{CrateType, Message, Metadata as CargoMetadata, Package, TargetKind};
use error::BuildActionError;
use mockall_double::double;
pub use package_task::SignMode;
use package_task::{PackageTask, PackageTaskParams};
use tracing::{debug, error as err, info, trace, warn};
use wdk_build::{
Expand All @@ -37,7 +38,7 @@ pub struct BuildActionParams<'a> {
pub working_dir: &'a Path,
pub profile: Option<&'a Profile>,
pub target_arch: Option<CpuArchitecture>,
pub verify_signature: bool,
pub sign_mode: SignMode,
pub is_sample_class: bool,
pub verbosity_level: clap_verbosity_flag::Verbosity,
}
Expand All @@ -48,7 +49,7 @@ pub struct BuildAction<'a> {
working_dir: PathBuf,
profile: Option<&'a Profile>,
target_arch: Option<CpuArchitecture>,
verify_signature: bool,
sign_mode: SignMode,
is_sample_class: bool,
verbosity_level: clap_verbosity_flag::Verbosity,

Expand Down Expand Up @@ -93,7 +94,7 @@ impl<'a> BuildAction<'a> {
working_dir: absolute(params.working_dir)?,
profile: params.profile,
target_arch: params.target_arch,
verify_signature: params.verify_signature,
sign_mode: params.sign_mode,
is_sample_class: params.is_sample_class,
verbosity_level: params.verbosity_level,
wdk_build,
Expand Down Expand Up @@ -386,7 +387,7 @@ impl<'a> BuildAction<'a> {
working_dir,
target_dir: &target_dir,
target_arch: &target_arch,
verify_signature: self.verify_signature,
sign_mode: self.sign_mode,
sample_class: self.is_sample_class,
driver_model,
},
Expand Down
62 changes: 51 additions & 11 deletions crates/cargo-wdk/src/actions/build/package_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ use windows::{
use crate::providers::{exec::CommandExec, fs::Fs, wdk_build::WdkBuild};
use crate::{actions::build::error::PackageTaskError, providers::error::FileError};

/// Signing mode.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SignMode {
/// Skip signing entirely.
Off,
/// Sign with an auto-generated self-signed certificate.
Test {
/// When `true`, run `signtool verify` on the signed driver binary and
/// catalog file after signing.
verify_signature: bool,
},
}

// FIXME: This range is inclusive of 25798. Update with range end after /sample
// flag is added to InfVerif CLI
const MISSING_SAMPLE_FLAG_WDK_BUILD_NUMBER_RANGE: RangeFrom<u32> = 25798..;
Expand All @@ -43,15 +56,15 @@ pub struct PackageTaskParams<'a> {
pub working_dir: &'a Path,
pub target_dir: &'a Path,
pub target_arch: &'a CpuArchitecture,
pub verify_signature: bool,
pub sign_mode: SignMode,
pub sample_class: bool,
pub driver_model: DriverConfig,
}

/// Supports low level driver packaging operations
pub struct PackageTask<'a> {
package_name: String,
verify_signature: bool,
sign_mode: SignMode,
sample_class: bool,

// src paths
Expand Down Expand Up @@ -161,7 +174,7 @@ impl<'a> PackageTask<'a> {

Self {
package_name,
verify_signature: params.verify_signature,
sign_mode: params.sign_mode,
sample_class: params.sample_class,
src_inx_file_path,
src_driver_binary_file_path,
Expand Down Expand Up @@ -236,6 +249,20 @@ impl<'a> PackageTask<'a> {
self.copy(&self.src_map_file_path, &self.dest_map_file_path)?;
self.run_stampinf()?;
self.run_inf2cat()?;
self.run_infverif()?;
self.sign_and_verify()?;
Ok(())
}

/// Signs the driver binary and catalog file according to `self.sign_mode`
/// and optionally verifies the resulting signatures. Returns a variant of
/// `PackageTaskError` if any step of the process fails.
fn sign_and_verify(&self) -> Result<(), PackageTaskError> {
let SignMode::Test { verify_signature } = self.sign_mode else {
info!("Sign mode is 'off'; skipping signing");
return Ok(());
};

self.generate_certificate()?;
self.copy(&self.src_cert_file_path, &self.dest_cert_file_path)?;
self.run_signtool_sign(
Expand All @@ -248,13 +275,13 @@ impl<'a> PackageTask<'a> {
WDR_TEST_CERT_STORE,
WDR_LOCAL_TEST_CERT,
)?;
self.run_infverif()?;
// Verify signatures only when --verify-signature flag = true is passed
if self.verify_signature {

if verify_signature {
info!("Verifying signatures for driver binary and cat file using signtool");
self.run_signtool_verify(&self.dest_driver_binary_path)?;
self.run_signtool_verify(&self.dest_cat_file_path)?;
}

Ok(())
}

Expand Down Expand Up @@ -635,7 +662,9 @@ mod tests {
target_arch: &arch,
driver_model: DriverConfig::Kmdf(KmdfConfig::default()),
sample_class: false,
verify_signature: false,
sign_mode: SignMode::Test {
verify_signature: false,
},
};
let dest_root = target_dir.join(format!("{package_name}_package"));

Expand All @@ -644,7 +673,12 @@ mod tests {
let fs = Fs::default();
let task = PackageTask::new(package_task_params, &wdk_build, &command_exec, &fs);
assert_eq!(task.package_name, package_name.replace('-', "_"));
assert!(!task.verify_signature);
assert_eq!(
task.sign_mode,
SignMode::Test {
verify_signature: false,
}
);
assert!(!task.sample_class);
assert_eq!(task.src_inx_file_path, working_dir.join("test_package.inx"));
assert_eq!(
Expand Down Expand Up @@ -698,7 +732,9 @@ mod tests {
target_arch: &arch,
driver_model: DriverConfig::Kmdf(KmdfConfig::default()),
sample_class: false,
verify_signature: false,
sign_mode: SignMode::Test {
verify_signature: false,
},
};

let command_exec = CommandExec::default();
Expand All @@ -724,7 +760,9 @@ mod tests {
target_arch: &arch,
driver_model: DriverConfig::Kmdf(KmdfConfig::default()),
sample_class: false,
verify_signature: false,
sign_mode: SignMode::Test {
verify_signature: false,
},
};

let command_exec = CommandExec::default();
Expand Down Expand Up @@ -759,7 +797,9 @@ mod tests {
target_arch: &arch,
driver_model: DriverConfig::Kmdf(KmdfConfig::default()),
sample_class: false,
verify_signature: false,
sign_mode: SignMode::Test {
verify_signature: false,
},
};

let wdk_build = WdkBuild::default();
Expand Down
87 changes: 78 additions & 9 deletions crates/cargo-wdk/src/actions/build/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use crate::providers::{
use crate::{
actions::{
Profile,
build::{BuildAction, BuildActionParams, error::BuildActionError},
build::{BuildAction, BuildActionParams, SignMode, error::BuildActionError},
to_target_triple,
},
providers::error::{CommandError, FileError},
Expand Down Expand Up @@ -265,6 +265,43 @@ pub fn given_a_driver_project_when_verify_signature_is_true_then_it_builds_succe
);
}

#[test]
pub fn given_a_driver_project_when_sign_mode_is_off_then_signing_and_verification_steps_are_skipped()
{
// Input CLI args
let cwd = PathBuf::from("C:\\tmp");
let profile = None;
let target_arch = CpuArchitecture::Amd64;
let verify_signature = false;
let sample_class = false;

// Driver project data
let driver_type = "KMDF";
let driver_name = "sample-kmdf";
let driver_version = "0.0.1";
let wdk_metadata = get_cargo_metadata_wdk_metadata(driver_type, 1, 33);
let (workspace_member, package) =
get_cargo_metadata_package(&cwd, driver_name, driver_version, Some(&wdk_metadata));

let cargo_build_output =
create_cargo_build_output_json(driver_name, driver_version, &cwd, None, profile);
let test_build_action = &TestBuildAction::new(cwd.clone(), profile, None, sample_class)
.with_sign_mode(SignMode::Off)
.set_up_standalone_driver_project((workspace_member, package))
.expect_default_build_task_steps(driver_name, Some(cargo_build_output))
.expect_probe_target_arch_using_cargo_rustc(&cwd, target_arch, None)
.expect_package_task_steps_with_sign_mode_off(driver_name, driver_type, target_arch);

assert_build_action_run_with_env_is_success(
&cwd,
profile,
None,
verify_signature,
sample_class,
test_build_action,
);
}

#[test]
pub fn given_a_driver_project_when_self_signed_exists_then_it_should_skip_calling_makecert() {
// Input CLI args
Expand Down Expand Up @@ -657,6 +694,7 @@ pub fn given_a_driver_project_when_certmgr_command_execution_fails_then_package_
.expect_copy_map_file_to_package_folder(driver_name, &cwd, true)
.expect_stampinf(driver_name, &cwd, target_arch, None)
.expect_inf2cat(driver_name, &cwd, target_arch, None)
.expect_infverif(driver_name, &cwd, driver_type, None)
.expect_self_signed_cert_file_exists(&cwd, false)
.expect_certmgr_exists_check(Some(expected_output));

Expand Down Expand Up @@ -716,6 +754,7 @@ pub fn given_a_driver_project_when_makecert_command_execution_fails_then_package
.expect_copy_map_file_to_package_folder(driver_name, &cwd, true)
.expect_stampinf(driver_name, &cwd, target_arch, None)
.expect_inf2cat(driver_name, &cwd, target_arch, None)
.expect_infverif(driver_name, &cwd, driver_type, None)
.expect_self_signed_cert_file_exists(&cwd, false)
.expect_certmgr_exists_check(None)
.expect_makecert(&cwd, Some(expected_output));
Expand Down Expand Up @@ -776,6 +815,7 @@ pub fn given_a_driver_project_when_signtool_command_execution_fails_then_package
.expect_copy_map_file_to_package_folder(driver_name, &cwd, true)
.expect_stampinf(driver_name, &cwd, target_arch, None)
.expect_inf2cat(driver_name, &cwd, target_arch, None)
.expect_infverif(driver_name, &cwd, driver_type, None)
.expect_self_signed_cert_file_exists(&cwd, false)
.expect_certmgr_exists_check(None)
.expect_makecert(&cwd, None)
Expand Down Expand Up @@ -838,13 +878,7 @@ pub fn given_a_driver_project_when_infverif_command_execution_fails_then_package
.expect_copy_map_file_to_package_folder(driver_name, &cwd, true)
.expect_stampinf(driver_name, &cwd, target_arch, None)
.expect_inf2cat(driver_name, &cwd, target_arch, None)
.expect_self_signed_cert_file_exists(&cwd, false)
.expect_certmgr_exists_check(None)
.expect_makecert(&cwd, None)
.expect_copy_self_signed_cert_file_to_package_folder(driver_name, &cwd, true)
.expect_signtool_sign_driver_binary_sys_file(driver_name, &cwd, None)
.expect_signtool_sign_cat_file(driver_name, &cwd, None)
.expect_infverif(driver_name, &cwd, "KMDF", Some(expected_output));
.expect_infverif(driver_name, &cwd, driver_type, Some(expected_output));

let build_action = initialize_build_action(
&cwd,
Expand Down Expand Up @@ -1594,12 +1628,16 @@ fn initialize_build_action<'a>(
sample_class: bool,
test_build_action: &'a TestBuildAction,
) -> Result<BuildAction<'a>, anyhow::Error> {
let sign_mode = match test_build_action.sign_mode {
SignMode::Off => SignMode::Off,
SignMode::Test { .. } => SignMode::Test { verify_signature },
};
BuildAction::new(
&BuildActionParams {
working_dir: cwd,
profile,
target_arch,
verify_signature,
sign_mode,
is_sample_class: sample_class,
verbosity_level: clap_verbosity_flag::Verbosity::new(1, 0),
},
Expand Down Expand Up @@ -1662,6 +1700,7 @@ struct TestBuildAction {
profile: Option<Profile>,
target_arch: Option<CpuArchitecture>,
sample_class: bool,
sign_mode: SignMode,

cargo_metadata: Option<CargoMetadata>,
// mocks
Expand All @@ -1688,6 +1727,9 @@ impl TestBuildAction {
profile,
target_arch,
sample_class,
sign_mode: SignMode::Test {
verify_signature: false,
},
mock_run_command,
mock_wdk_build_provider,
mock_fs_provider,
Expand All @@ -1696,6 +1738,11 @@ impl TestBuildAction {
}
}

fn with_sign_mode(mut self, sign_mode: SignMode) -> Self {
self.sign_mode = sign_mode;
self
}

fn set_up_standalone_driver_project(
mut self,
package_metadata: (TestMetadataWorkspaceMemberId, TestMetadataPackage),
Expand Down Expand Up @@ -1826,6 +1873,28 @@ impl TestBuildAction {
.expect_signtool_verify_cat_file(driver_name, &cwd, None)
}

/// Sets up package-task expectations for `SignMode::Off`: stampinf,
/// inf2cat, and infverif are still expected, but all certificate
/// generation, signing, and signature-verification steps are skipped.
fn expect_package_task_steps_with_sign_mode_off(
self,
driver_name: &str,
driver_type: &str,
target_arch: CpuArchitecture,
) -> Self {
let cwd = self.cwd.clone();
self.expect_final_package_dir_exists(driver_name, &cwd, true)
.expect_inx_file_exists(driver_name, &cwd, true)
.expect_rename_driver_binary_dll_to_sys(driver_name, &cwd)
.expect_copy_driver_binary_sys_to_package_folder(driver_name, &cwd, true)
.expect_copy_pdb_file_to_package_folder(driver_name, &cwd, true)
.expect_copy_inx_file_to_package_folder(driver_name, &cwd, true, &cwd)
.expect_copy_map_file_to_package_folder(driver_name, &cwd, true)
.expect_stampinf(driver_name, &cwd, target_arch, None)
.expect_inf2cat(driver_name, &cwd, target_arch, None)
.expect_infverif(driver_name, &cwd, driver_type, None)
}

fn expect_default_package_task_steps_for_workspace(
self,
driver_name: &str,
Expand Down
Loading
Loading