From bfb7c084ac8713f0ec35894f179b56a7d2c9ff38 Mon Sep 17 00:00:00 2001 From: "omer.roth" Date: Sun, 2 Aug 2026 14:46:54 +0300 Subject: [PATCH 1/6] CM-70024: rotate Windows code signing cert DigiCert issued a new Cycode Ltd code signing cert (valid 2026-06-05 to 2027-08-22). Point signtool at its thumbprint via the temporary SM_CODE_SIGNING_CERT_SHA1_HASH_NEW secret so the old value stays intact as a fallback until a CI run confirms the new cert signs successfully. Also drop --keypair-alias from certsync. The previous attempt failed with "Certificate for keypair alias: *** not found", which synced zero certs and left signtool with no candidates. Syncing every cert in the account and letting /sha1 pick removes an extra value to keep in sync on renewal. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/build_executable.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build_executable.yml b/.github/workflows/build_executable.yml index f1600611..e127c357 100644 --- a/.github/workflows/build_executable.yml +++ b/.github/workflows/build_executable.yml @@ -236,16 +236,19 @@ jobs: shell: cmd env: SM_HOST: ${{ secrets.SM_HOST }} - SM_KEYPAIR_ALIAS: ${{ secrets.SM_KEYPAIR_ALIAS }} SM_API_KEY: ${{ secrets.SM_API_KEY }} SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }} - SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }} + # TODO(CM-70024): revert to SM_CODE_SIGNING_CERT_SHA1_HASH once the new cert is confirmed working + SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH_NEW }} run: | :: setup SSM KSP - curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/smtools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o smtools-windows-x64.msi + curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/smtools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o smtools-windows-x64.msi msiexec /i smtools-windows-x64.msi /quiet /qn C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user - smctl windows certsync --keypair-alias=%SM_KEYPAIR_ALIAS% + + :: sync every cert in the account into the Windows store; signtool /sha1 below + :: selects the right one, so there is no keypair alias to keep in sync on renewal + smctl windows certsync :: sign executable (in onedir mode the exe lives inside the collected directory) set "EXE_PATH=.\dist\cycode-cli.exe" @@ -259,7 +262,8 @@ jobs: SM_HOST: ${{ secrets.SM_HOST }} SM_API_KEY: ${{ secrets.SM_API_KEY }} SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }} - SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }} + # TODO(CM-70024): revert to SM_CODE_SIGNING_CERT_SHA1_HASH once the new cert is confirmed working + SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH_NEW }} run: | # Vendor binaries (PSF-signed stdlib .pyds, python3xx.dll, Microsoft VC runtime) already # carry valid signatures; re-signing would replace them with ours. Sign only the unsigned From 556d832feb851386fc666511325b138d1879f6c9 Mon Sep 17 00:00:00 2001 From: "omer.roth" Date: Sun, 2 Aug 2026 14:52:47 +0300 Subject: [PATCH 2/6] CM-70024: sync the new signing cert by keypair alias certsync without an alias only synced the first keypair the account returns (the old cert), so signtool still had no match for the new thumbprint. Restore --keypair-alias, sourced from SM_KEYPAIR_ALIAS_NEW. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/build_executable.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_executable.yml b/.github/workflows/build_executable.yml index e127c357..306c061d 100644 --- a/.github/workflows/build_executable.yml +++ b/.github/workflows/build_executable.yml @@ -238,7 +238,8 @@ jobs: SM_HOST: ${{ secrets.SM_HOST }} SM_API_KEY: ${{ secrets.SM_API_KEY }} SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }} - # TODO(CM-70024): revert to SM_CODE_SIGNING_CERT_SHA1_HASH once the new cert is confirmed working + # TODO(CM-70024): revert to the non-_NEW secrets once the new cert is confirmed working + SM_KEYPAIR_ALIAS: ${{ secrets.SM_KEYPAIR_ALIAS_NEW }} SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH_NEW }} run: | :: setup SSM KSP @@ -246,9 +247,9 @@ jobs: msiexec /i smtools-windows-x64.msi /quiet /qn C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user - :: sync every cert in the account into the Windows store; signtool /sha1 below - :: selects the right one, so there is no keypair alias to keep in sync on renewal - smctl windows certsync + :: certsync without an alias only syncs the first keypair the account returns, + :: so the alias is required to get the current signing cert into the store + smctl windows certsync --keypair-alias=%SM_KEYPAIR_ALIAS% :: sign executable (in onedir mode the exe lives inside the collected directory) set "EXE_PATH=.\dist\cycode-cli.exe" From 6a33deee86f6d71c2aa133fd3e0848440d13eb90 Mon Sep 17 00:00:00 2001 From: "omer.roth" Date: Sun, 2 Aug 2026 15:00:07 +0300 Subject: [PATCH 3/6] CM-70024: temporary smctl diagnostics for cert sync The keypair is visible in the KSP but smctl reports its certificate as not found. List keypairs and certificates in CI to see the association and status. To be removed before merge. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/build_executable.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build_executable.yml b/.github/workflows/build_executable.yml index 306c061d..9a4a9ca4 100644 --- a/.github/workflows/build_executable.yml +++ b/.github/workflows/build_executable.yml @@ -247,6 +247,11 @@ jobs: msiexec /i smtools-windows-x64.msi /quiet /qn C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user + :: TODO(CM-70024): temporary diagnostics, remove before merge + smctl healthcheck --all + smctl keypair ls + smctl certificate ls + :: certsync without an alias only syncs the first keypair the account returns, :: so the alias is required to get the current signing cert into the store smctl windows certsync --keypair-alias=%SM_KEYPAIR_ALIAS% From ea83882bd146f57e4f040374727a76c939a7acb7 Mon Sep 17 00:00:00 2001 From: "omer.roth" Date: Sun, 2 Aug 2026 15:11:27 +0300 Subject: [PATCH 4/6] CM-70024: probe certsync lookup flags (temporary) Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/build_executable.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_executable.yml b/.github/workflows/build_executable.yml index 9a4a9ca4..b135d473 100644 --- a/.github/workflows/build_executable.yml +++ b/.github/workflows/build_executable.yml @@ -248,9 +248,13 @@ jobs: C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user :: TODO(CM-70024): temporary diagnostics, remove before merge - smctl healthcheck --all - smctl keypair ls - smctl certificate ls + smctl windows certsync --help + echo ==== hardcoded keypair alias ==== + smctl windows certsync --keypair-alias=key_1534117339 + echo ==== certificate alias ==== + smctl windows certsync --keypair-alias=cert_1534117339 + echo ==== windows store contents ==== + C:\Windows\System32\certutil.exe -user -store My :: certsync without an alias only syncs the first keypair the account returns, :: so the alias is required to get the current signing cert into the store From a959d09788e10bd61046504b73bab6031790550f Mon Sep 17 00:00:00 2001 From: "omer.roth" Date: Sun, 2 Aug 2026 15:33:59 +0300 Subject: [PATCH 5/6] CM-70024: source Signing Manager credentials from the service account Drop the probe steps now that the new cert signs. Point the client cert, its password and the API key at _NEW secrets so a service account can be verified in CI without disturbing main, and keep a temporary healthcheck to confirm which identity actually signs. Also record why certsync failed for the new cert: it only syncs keypairs the calling identity may sign with, so the credential owner must be a signer on the certificate. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/build_executable.yml | 30 +++++++++++--------------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build_executable.yml b/.github/workflows/build_executable.yml index b135d473..1f24b832 100644 --- a/.github/workflows/build_executable.yml +++ b/.github/workflows/build_executable.yml @@ -221,7 +221,8 @@ jobs: - name: Import cert for Windows and setup envs if: runner.os == 'Windows' env: - SM_CLIENT_CERT_FILE_B64: ${{ secrets.SM_CLIENT_CERT_FILE_B64 }} + # TODO(CM-70024): revert to the non-_NEW secrets once the service account is confirmed working + SM_CLIENT_CERT_FILE_B64: ${{ secrets.SM_CLIENT_CERT_FILE_B64_NEW }} run: | # import certificate echo "$SM_CLIENT_CERT_FILE_B64" | base64 --decode > /d/Certificate_pkcs12.p12 @@ -236,9 +237,9 @@ jobs: shell: cmd env: SM_HOST: ${{ secrets.SM_HOST }} - SM_API_KEY: ${{ secrets.SM_API_KEY }} - SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }} - # TODO(CM-70024): revert to the non-_NEW secrets once the new cert is confirmed working + # TODO(CM-70024): revert to the non-_NEW secrets once the new cert and service account are confirmed working + SM_API_KEY: ${{ secrets.SM_API_KEY_NEW }} + SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD_NEW }} SM_KEYPAIR_ALIAS: ${{ secrets.SM_KEYPAIR_ALIAS_NEW }} SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH_NEW }} run: | @@ -246,18 +247,11 @@ jobs: curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/smtools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o smtools-windows-x64.msi msiexec /i smtools-windows-x64.msi /quiet /qn C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user + :: TODO(CM-70024): temporary, remove before merge — confirms which identity signs + smctl healthcheck --all - :: TODO(CM-70024): temporary diagnostics, remove before merge - smctl windows certsync --help - echo ==== hardcoded keypair alias ==== - smctl windows certsync --keypair-alias=key_1534117339 - echo ==== certificate alias ==== - smctl windows certsync --keypair-alias=cert_1534117339 - echo ==== windows store contents ==== - C:\Windows\System32\certutil.exe -user -store My - - :: certsync without an alias only syncs the first keypair the account returns, - :: so the alias is required to get the current signing cert into the store + :: certsync only syncs keypairs the calling identity has signing access to, so the + :: identity behind SM_API_KEY must be a signer on the certificate for this alias smctl windows certsync --keypair-alias=%SM_KEYPAIR_ALIAS% :: sign executable (in onedir mode the exe lives inside the collected directory) @@ -270,9 +264,9 @@ jobs: shell: powershell env: SM_HOST: ${{ secrets.SM_HOST }} - SM_API_KEY: ${{ secrets.SM_API_KEY }} - SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }} - # TODO(CM-70024): revert to SM_CODE_SIGNING_CERT_SHA1_HASH once the new cert is confirmed working + # TODO(CM-70024): revert to the non-_NEW secrets once the new cert and service account are confirmed working + SM_API_KEY: ${{ secrets.SM_API_KEY_NEW }} + SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD_NEW }} SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH_NEW }} run: | # Vendor binaries (PSF-signed stdlib .pyds, python3xx.dll, Microsoft VC runtime) already From f84a8b508cb9c53992f3189cd56febdfb70f9b82 Mon Sep 17 00:00:00 2001 From: "omer.roth" Date: Sun, 2 Aug 2026 15:40:51 +0300 Subject: [PATCH 6/6] CM-70024: document the certsync signing-access requirement The cert rotation itself lives entirely in DigiCert One and the repo secrets, so all _NEW indirection is gone. What is worth keeping in the workflow is why certsync reported the new certificate as missing: it only syncs keypairs the calling identity may sign with. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/build_executable.yml | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build_executable.yml b/.github/workflows/build_executable.yml index 1f24b832..00b86fa9 100644 --- a/.github/workflows/build_executable.yml +++ b/.github/workflows/build_executable.yml @@ -221,8 +221,7 @@ jobs: - name: Import cert for Windows and setup envs if: runner.os == 'Windows' env: - # TODO(CM-70024): revert to the non-_NEW secrets once the service account is confirmed working - SM_CLIENT_CERT_FILE_B64: ${{ secrets.SM_CLIENT_CERT_FILE_B64_NEW }} + SM_CLIENT_CERT_FILE_B64: ${{ secrets.SM_CLIENT_CERT_FILE_B64 }} run: | # import certificate echo "$SM_CLIENT_CERT_FILE_B64" | base64 --decode > /d/Certificate_pkcs12.p12 @@ -237,18 +236,15 @@ jobs: shell: cmd env: SM_HOST: ${{ secrets.SM_HOST }} - # TODO(CM-70024): revert to the non-_NEW secrets once the new cert and service account are confirmed working - SM_API_KEY: ${{ secrets.SM_API_KEY_NEW }} - SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD_NEW }} - SM_KEYPAIR_ALIAS: ${{ secrets.SM_KEYPAIR_ALIAS_NEW }} - SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH_NEW }} + SM_KEYPAIR_ALIAS: ${{ secrets.SM_KEYPAIR_ALIAS }} + SM_API_KEY: ${{ secrets.SM_API_KEY }} + SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }} + SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }} run: | :: setup SSM KSP curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/smtools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o smtools-windows-x64.msi msiexec /i smtools-windows-x64.msi /quiet /qn C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user - :: TODO(CM-70024): temporary, remove before merge — confirms which identity signs - smctl healthcheck --all :: certsync only syncs keypairs the calling identity has signing access to, so the :: identity behind SM_API_KEY must be a signer on the certificate for this alias @@ -264,10 +260,9 @@ jobs: shell: powershell env: SM_HOST: ${{ secrets.SM_HOST }} - # TODO(CM-70024): revert to the non-_NEW secrets once the new cert and service account are confirmed working - SM_API_KEY: ${{ secrets.SM_API_KEY_NEW }} - SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD_NEW }} - SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH_NEW }} + SM_API_KEY: ${{ secrets.SM_API_KEY }} + SM_CLIENT_CERT_PASSWORD: ${{ secrets.SM_CLIENT_CERT_PASSWORD }} + SM_CODE_SIGNING_CERT_SHA1_HASH: ${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }} run: | # Vendor binaries (PSF-signed stdlib .pyds, python3xx.dll, Microsoft VC runtime) already # carry valid signatures; re-signing would replace them with ours. Sign only the unsigned