Skip to content

[STUD-79136]: Configurable PGP key size, IResource inputs, paired-property normalization#562

Draft
alexandru-petre wants to merge 64 commits into
developfrom
feature/cryptography-pgp-keysize-resource
Draft

[STUD-79136]: Configurable PGP key size, IResource inputs, paired-property normalization#562
alexandru-petre wants to merge 64 commits into
developfrom
feature/cryptography-pgp-keysize-resource

Conversation

@alexandru-petre
Copy link
Copy Markdown
Collaborator

Problem Statement

The Cryptography pack accumulated several pain points since the v2.0 release:

  1. PGP key generation hardcoded to RSA-1024, considered weak since 2010 (STUD-79136 calls for configurable key size, enterprise-grade default).
  2. No AEAD ciphers — only legacy CBC modes; modern interop expects AESGCM / ChaCha20-Poly1305.
  3. Symmetric-cipher wire format incompatibility with third-party PGP tooling — STUD-64429 surfaced cases where ServiceNow / devglan / OpenSSL ciphertext couldn't be decrypted (and vice versa) due to the deliberate PBKDF2-salt + random-IV format.
  4. No storage-resource (IResource) inputs — file activities only accepted local string paths.
  5. Three different patterns for path ↔ IResource and string ↔ SecureString toggles across the pack (switch-strict, switch-fallback, HasValue-inference); [OverloadGroup] on the existing PGP sign / clear-sign / verify activities was misused (validator rejected any XAML that bound both file + key inputs).
  6. NuGet packaging emits deprecation warnings on every build (NU5125, NU5048).
  7. Activity class names mismatch display names for the PGP activities, hurting Studio's search experience.
  8. PGP was silently treated as FIPS-compliant in IsFipsCompliant, even though the underlying PgpCore + BouncyCastle path is not a CMVP-validated module. The dropdown label and design-time warning didn't surface this either.

Analysis

The work splits into a few intertwined themes across the 39 Cryptography-scoped commits on this branch. The PGP signing primitives themselves (Sign / Clearsign / Verify / GenerateKey) were already on develop via PR #514; this PR makes them production-ready and unifies the pack:

  • PGP hardening (STUD-79136)RsaKeySize enum (2048 / 3072 / 4096) defaulting to 4096; SHA-256 hashing; AES-256 session keys; renamed PgpGenerateKeyPair.Username → UserId to match OpenPGP convention.
  • AEAD cipher modernization — added AESGCM and ChaCha20-Poly1305; marked the older CBC ciphers obsolete; new default for EncryptionAlgorithm is AESGCM.
  • Symmetric interop diagnostics (STUD-64429) — documented the wire format (docs/symmetric-wire-format.md) and emit a localized hint pointing at the spec when foreign-format ciphertext fails to decrypt.
  • IResource storage inputs — paired path / IResource properties on every file activity (Encrypt / Decrypt / Hash / Pgp*) with viewmodel menu-action toggles and runtime resolution.
  • SecureString passphrase support — paired plain / secure passphrase properties on all six PGP-passphrase-bearing activities.
  • OverloadGroup fix on PGP sign / clear-sign / verify — distinct [OverloadGroup] names made the file + key pairs mutually exclusive at the validator level, which broke every valid XAML. Replaced with runtime fallback resolution.
  • Paired-property normalization — every pair across the pack now follows one template: no dedicated *InputModeSwitch enum, viewmodel uses pure HasValue-inference for initial mode, runtime resolves via string-first fallback. Kept obsolete switches that existed on origin/masters/Cryptography for XAML deserialization compatibility; deleted switches that were only ever added since the last release.
  • PGP activity renames for Studio search alignment: PgpGenerateKeyPair → PgpGenerateKeys (matches display "PGP Generate Keys"), PgpClearSignFile → PgpClearsignFile (matches GnuPG --clearsign spelling and RFC 9580 "cleartext signature"). PgpSignFile and PgpVerify already matched.
  • FIPS classification correctnessIsFipsCompliant(PGP) now returns false (PgpCore + open-source BouncyCastle is not a CMVP-validated module). The dropdown label changed PGP (Pretty Good Privacy)PGP - Pretty Good Privacy (Non-FIPS) to match how other non-FIPS ciphers are labeled, and the existing yellow design-time FipsComplianceWarning now also surfaces when PGP is selected (the Algorithm != PGP && short-circuit was removed from the four CacheMetadata methods).
  • Packaging cleanup — removed deprecated <PackageLicenseUrl> / <PackageIconUrl> in favor of <PackageLicenseFile> (the existing LICENSE.md now ships in the .nupkg) and <PackageProjectUrl>.

Considered Use Cases

  • PGP key generation: RSA-2048 / 3072 / 4096; legacy XAML omitting KeySize defaults to 4096.
  • Symmetric encryption: AESGCM / ChaCha20-Poly1305 (new), AES / DES / RC2 / Rijndael / TripleDES (obsolete but still functional for back-compat).
  • PGP encrypt / decrypt with optional sign / verify; passphrase as string or SecureString.
  • PGP sign / clearsign / verify: binary signature, clearsign cleartext signature, public-key validation modes.
  • IResource inputs: storage-bucket resource provided in place of a local file path (input file, public / private key file).
  • Three customer workflows reported during development: full sign → clearsign → verify pipeline; EncryptText + DecryptText with sign + verify; EncryptFile + DecryptFile with sign + verify — all three covered by new end-to-end regression tests.
  • Interop with third-party PGP tooling (STUD-64429): runtime error now hints at the wire-format spec instead of producing an opaque CryptographicException.
  • FIPS-locked machines: PGP now visibly flagged as non-FIPS in the dropdown and triggers the standard design-time warning, so operators can make an informed choice before deployment.

Implementation

New files (4 truly new + 3 renamed-with-rewrite):

  • UiPath.Cryptography/Enums/RsaKeySize.cs — 2048 / 3072 / 4096.
  • UiPath.Cryptography.Activities/Helpers/PgpFileResolver.cs — shared string-first fallback for path ↔ IResource and string ↔ SecureString.
  • Activities/Cryptography/docs/symmetric-wire-format.md — STUD-64429 spec.
  • UiPath.Cryptography.Activities.Tests/Resources/ActivityMetadataTests.cs — metadata invariant checks.
  • PgpGenerateKeys.cs (renamed + rewritten from PgpGenerateKeyPair.cs), PgpClearsignFile.cs (renamed + rewritten from PgpClearSignFile.cs), and the matching viewmodels.

Modified (~57 files):

  • All file-based Encrypt / Decrypt / Hash / PGP activities — IResource pair properties, OutputFilePath fallback to input directory, sentence-case display-name fixes, paired-property template normalization, uniform IsFipsCompliant check (no PGP short-circuit).
  • ViewModels — pure HasValue-inference for initial mode, switch handlers null the inactive side, no writes to obsolete *InputModeSwitch enums.
  • CryptographyHelper.cs — added AESGCM / ChaCha20-Poly1305; documented obsolete-cipher rationale; corrected IsFipsCompliant(PGP) to return false.
  • UiPath.Cryptography.resxPGP label gets the (Non-FIPS) suffix, parens around the long form replaced with a hyphen for legibility.
  • ActivitiesMetadata.json, English .resx, Designer.cs accessors — kept in lock-step with class / property renames and switch deletions.

Removed: deprecated <PackageLicenseUrl> / <PackageIconUrl> in the Packaging csproj.

Caveats / Potential Issues

  • IsFipsCompliant(PGP) semantics flipped from true to false. This now produces an observable yellow design-time warning when the algorithm is selected — that's the intended behavior, but it is a new warning that didn't appear in v2.0 for workflows that had been authored against PGP.
  • Locale .resx files are intentionally stale for new keys added on this branch (Passphrase / Passphrase Secure, IResource displays, UserId, normalized pair entries). The resource manager falls back to English for missing keys. A translation pass is a follow-up.
  • Kept-obsolete switches (KeyInputModeSwitch on Encrypt / Decrypt / Hash, FileInputModeSwitch on Encrypt / Decrypt) are still declared on the activity class for XAML back-compat with already-published workflows. They're [Browsable(false)] [Obsolete] and never read by the viewmodel or runtime — deserialization is by construction (property exists on the class), not verified by a load-an-XAML test.
  • Async PGP sign / clearsign / verify: no design-time validation for the file path ↔ IResource pair (runtime fallback handles it). Matches the rest of the pack now.
  • OutputFileName + OutputFilePath both set: pre-existing behavior preserved (FileName silently ignored when FilePath is also set). Documented as an open item; no reported customer impact.

How to Test

  1. dotnet build Activities/Activities.Cryptography.sln — expect 0 errors and 1 pre-existing unrelated CS1998 warning (CryptographyLocalItem.cs:73). NU5125 / NU5048 should be gone.
  2. dotnet test Activities/Activities.Cryptography.sln — expect 518 / 518 passing.
  3. Targeted customer-workflow regressions: dotnet test --filter "FullyQualifiedName~RoundTrip|FullyQualifiedName~EndToEnd" — 12 passing (covers the three customer XAMLs reported during development).
  4. Manual smoke in Studio:
    • Drop PGP Generate Keys; verify the KeySize dropdown shows 2048 / 3072 / 4096 and defaults to 4096.
    • Sign → Verify with PGP Sign File + PGP Verify (binary mode).
    • Clearsign → Verify with PGP Clearsign File + PGP Verify (Clearsigned File mode).
    • Search the activity panel for PGP and Generate Keys — both should surface the activity (renames make the class names match the display names).
    • On Encrypt File (or any of the four symmetric activities), open the Algorithm dropdown — PGP - Pretty Good Privacy (Non-FIPS) should be visible. Select it: the yellow FipsComplianceWarning should appear in the designer next to the activity.

alexandru-petre and others added 30 commits March 31, 2026 15:29
…URE, PITFALLS, SUMMARY)

Synthesized findings from all four parallel research agents into unified SUMMARY.md.
Includes executive summary, key findings by dimension, roadmap implications,
confidence assessment, and open questions for phase planning.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…eyPair

- Create RsaKeySize enum with Rsa2048, Rsa3072, Rsa4096 members and [LocalizedDescription] attributes
- Add KeySize InArgument<RsaKeySize> property to PgpGenerateKeyPair activity with [DefaultValue(RsaKeySize.Rsa4096)]
- Place KeySize in Options category (IsPrincipal=false) after Overwrite property
- Add KeySize DesignProperty to PgpGenerateKeyPairViewModel with dropdown widget
- Add localization resource keys for enum values and property labels
- Update Designer.cs files with generated resource accessors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tion

- Updated STATE.md with plan advancement (plan 1→2 complete, progress 50%)
- Updated ROADMAP.md with phase 2 plan 1 progress
- Marked requirements KEYGEN-01, DESIGN-01 as complete
- Created 02-01-SUMMARY.md with full execution details

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…copy-paste-artifacts

# Conflicts:
#	Activities/Credentials/UiPath.Credentials.Activities.ViewModels/Resources/ActivitiesMetadataWindows.json
…Metadata.json

- Added KeySize property entry to PgpGenerateKeyPair activity metadata
- Configured with Options category, DisplayNameKey and TooltipKey references
- Positioned after Overwrite and before ContinueOnError properties
- JSON validation passed
… entries

- Added Activity_PgpGenerateKeyPair_Property_KeySize_Name = 'Key Size'
- Added Activity_PgpGenerateKeyPair_Property_KeySize_Description
- Added RsaKeySize_Rsa2048, RsaKeySize_Rsa3072, RsaKeySize_Rsa4096 enum member entries
- Entries follow standard .resx XML format with xml:space='preserve' attribute
…tion and localization

- Created 02-02-SUMMARY.md with task completion details and verification results
- Advanced plan counter from 02 to 03
- Updated progress from 50% to 75% (3/4 plans complete)
- Marked DESIGN-02 and DESIGN-03 requirements complete
- Routed files to appropriate repositories via commit-to-subrepo
- Updated PackageReference in Directory.build.targets
- Enables keySize parameter support in PgpCore GenerateKey() method

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…KeyPair() helper

- Updated method signature to accept RsaKeySize keySize parameter with default Rsa4096
- Passes (int)keySize to pgp.GenerateKey() call
- Added using statement for UiPath.Cryptography.Enums namespace

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lper

- Added keySizeValue read from KeySize property via this.KeySize.Get(context)
- Updated CryptographyHelper.PgpGenerateKeyPair() call to pass keySizeValue parameter
- Completes data flow from designer property to key generation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…256, AES-256)

- Added using statement for Org.BouncyCastle.Bcpg
- PgpEncryptStream: set HashAlgorithmTag.Sha256 and SymmetricKeyAlgorithmTag.Aes256
- PgpEncryptText: set HashAlgorithmTag.Sha256 and SymmetricKeyAlgorithmTag.Aes256
- Aligns encryption with RFC 9580 and 2025 enterprise security standards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…256, AES-256)

- PgpDecryptStream: set HashAlgorithmTag.Sha256 and SymmetricKeyAlgorithmTag.Aes256
- PgpDecryptText: set HashAlgorithmTag.Sha256 and SymmetricKeyAlgorithmTag.Aes256
- Ensures consistent enterprise-grade defaults across encrypt/decrypt pipeline
- Aligns decryption with RFC 9580 and 2025 enterprise security standards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Added 02-03-SUMMARY.md documenting all 5 task completions
- Updated STATE.md with phase progress (100% complete)
- Marked requirements KEYGEN-02, AUDIT-01, AUDIT-02 as complete
- Updated ROADMAP.md phase 02 status to Complete

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…kward compatibility

Phase 3 validates the KeySize implementation from Phase 2 through comprehensive parameterized testing:
- Update PgpTestBase to accept optional keySize parameter (default Rsa4096)
- Add [Theory] tests for all three key sizes (2048, 3072, 4096)
- Add [Fact] backward compatibility test (activity without KeySize → 4096-bit default)
- Add encrypt/decrypt roundtrip tests for all key sizes
- Validate all five phase 3 requirements: KEYGEN-03, AUDIT-03, TEST-01, TEST-02, TEST-03

Plan is autonomous (3 tasks, no checkpoints), 1 wave.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…PgpTestBase

- Add RsaKeySize import
- Update constructor to accept optional keySize parameter with default RsaKeySize.Rsa4096
- Pass (int)keySize to pgp.GenerateKey() call
- Maintains backward compatibility with existing test subclasses
…n PgpStandaloneTests

- Add [Theory] test PgpGenerateKeyPair_WithKeySize_CreatesValidKey with [InlineData] for all three key sizes
- Validates key generation with CryptographyHelper.PgpGenerateKeyPair(keySize parameter)
- Add [Fact] test PgpGenerateKeyPair_Activity_WithoutKeySize_DefaultsTo4096 for backward compatibility
- Verifies Activity defaults to Rsa4096 when KeySize property not set
…roundtrip tests in PgpTests

- Add [Theory] test PgpEncryptDecryptText_WithGeneratedKeySize_Roundtrip with [InlineData] for all three key sizes
- Generates keys at each size, encrypts plaintext with generated keys, decrypts and validates roundtrip
- Validates TEST-03 and AUDIT-03: encrypt/decrypt operations work with all key sizes
…d backward compatibility

- Created 03-SUMMARY.md with complete test coverage validation
- Updated STATE.md to mark Phase 3 and entire project as complete
- Updated ROADMAP.md with Phase 3 completion status and all requirements validated
- All 12 requirements satisfied across all phases (100% coverage)
- 5 test methods added: 1 PgpTestBase + 2 PgpStandaloneTests + 1 PgpTests
- Parameterized testing validates all 3 RSA key sizes (2048, 3072, 4096)
- Backward compatibility verified via Activity default behavior
- Cross-size encrypt/decrypt validated for full interoperability
…tivity with dual input properties

- Changed PgpSignFile base class from CodeActivity to AsyncTaskCodeActivity
- Added InputFile property (IResource) with OverloadGroup(InputFilePath)
- Added FileInputModeSwitch property to toggle between FilePath and File modes
- Added PrivateKeyFile property (IResource) with OverloadGroup(PrivateKeyFilePath)
- Added PrivateKeyFileInputModeSwitch property for independent key file mode switching
- Replaced synchronous Execute method with async ExecuteAsync override
- Implemented IResource resolution with await ResolveAsync() for both file inputs
- Added explicit null/whitespace validation for resolved paths before helper call
- Added required imports: System.Threading, System.Threading.Tasks, UiPath.Cryptography.Enums
- ExecuteAsync resolves resources to strings before calling PgpFileSignHelper.ExecuteSign unchanged

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…gnViewModelBase

- Added four backup fields for preserving values during mode switching
- Added InputFile and FileInputModeSwitch design properties
- Added PrivateKeyFile and PrivateKeyFileInputModeSwitch design properties
- Initialized all new properties in InitializeModel with correct IsPrincipal/IsVisible flags
- Added two MenuActionsBuilder chains for independent file input mode switching
- Backed up initial values in InitializeModel for toggle preservation
- Added InitializeRules override to register both mode-switch action rules
- Added ManualRegisterDependencies override to register dependencies for both mode switches
- Implemented FileInputModeChanged_Action with backup/restore visibility toggle logic
- Implemented PrivateKeyFileInputModeChanged_Action with independent toggle logic
- Added required imports: System, System.Activities, UiPath.Cryptography.Enums

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nput mode properties

- Added .resx entries for InputFile, FileInputModeSwitch, PrivateKeyFile, and PrivateKeyFileInputModeSwitch
- Added corresponding Designer.cs property accessors for resource lookups
- Fixed ExecuteAsync access modifier from protected internal to protected
- All resource strings follow existing naming and description patterns

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 2 tasks completed: PgpSignFile migration to AsyncTaskCodeActivity, PgpSignViewModelBase enhancement
- All file inputs (InputFilePath/InputFile, PrivateKeyFilePath/PrivateKeyFile) support dual input modes
- Independent mode switches allow flexible designer experience
- Backup/restore pattern preserves user values across mode toggles
- MenuActionsBuilder integration creates intuitive mode-switching UI
- Build succeeds with Release configuration
- Next: Plan 04-02 for metadata registration and validator support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
alexandru-petre and others added 30 commits April 20, 2026 12:40
… for PgpVerify

- Added InputFile property with DisplayName and Tooltip keys
- Added FileInputModeSwitch property for mode toggling
- Added PublicKeyFile property with DisplayName and Tooltip keys
- Added PublicKeyFileInputModeSwitch property for independent key file mode switching
- All properties marked with IsVisible=false and proper Category assignment
- Metadata structure matches pattern from PgpSignFile and PgpClearSignFile

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…nput mode

Plan 04-04 complete:
- Registered InputFile, FileInputModeSwitch, PublicKeyFile, PublicKeyFileInputModeSwitch in ActivitiesMetadata.json
- Verified all 8 localization entries present in Resources.resx from plan 04-03
- Build succeeded with no errors
- All three PGP file-based activities now have complete dual-input mode support

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Advanced plan counter to 04-04 (4 of 5)
- Updated progress to 90% (9 of 10 plans completed)
- Updated ROADMAP.md with plan 04-04 SUMMARY.md completion

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…ties

- Fixed existing test bugs: extract result from WorkflowInvoker dictionary
- Added reflection-based tests to verify IResource properties exist
- Verified all three PGP activities (PgpSignFile, PgpClearSignFile, PgpVerify) have:
  - InputFile/PublicKeyFile IResource properties
  - FileInputModeSwitch and file-specific mode switches
  - Proper OverloadGroup attributes
- Documented OverloadGroup validation limitation preventing mixed-mode testing
- Test suite now has 16 passing tests (up from baseline)

Deferred: Full IResource roundtrip testing via WorkflowInvoker requires fixing
activity OverloadGroup validation design (out of scope for this plan).
…tion

Verification found that PgpClearSignFile has dual input mode properties
defined but still uses synchronous CodeActivity base class. ExecuteAsync
method never implemented; IResource inputs (InputFile, PrivateKeyFile)
are silently ignored at runtime. Plan 04-06 closes this gap by migrating
PgpClearSignFile to AsyncTaskCodeActivity with ExecuteAsync and proper
IResource resolution using the proven PgpSignFile reference pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…Resource resolution

- Changed base class from CodeActivity to AsyncTaskCodeActivity
- Added System.Threading and System.Threading.Tasks imports
- Replaced Execute() with async ExecuteAsync(AsyncCodeActivityContext, CancellationToken)
- Implemented IResource resolution for both InputFile and PrivateKeyFile
- Both files resolved via await ResolveAsync() pattern matching PgpSignFile reference
- Validated resolved paths before passing to PgpFileSignHelper.ExecuteSign()
- Preserved exception handling and telemetry patterns
- Code compiles without errors
- Closes verification gap: FileInputModeSwitch and PrivateKeyFileInputModeSwitch now functional at runtime
…n plan

- Migrated PgpClearSignFile from CodeActivity to AsyncTaskCodeActivity
- Implemented async ExecuteAsync() with IResource resolution for InputFile and PrivateKeyFile
- Both files now properly resolved via await ResolveAsync() pattern
- Matches PgpSignFile reference implementation exactly
- Closes verification gap: FileInputModeSwitch and PrivateKeyFileInputModeSwitch now functional
- Phase 04 objective 100% achieved: all file-based activities support dual input mode
- Archive ROADMAP and REQUIREMENTS to .planning/milestones/v1.0-*
- Add MILESTONES.md index with v1.0 entry
- Collapse ROADMAP.md to milestone summary
- Update PROJECT.md to current-state format
- Reset STATE.md to milestone-complete

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Archived to .planning/milestones/v1.0-REQUIREMENTS.md.
Next milestone will create a fresh REQUIREMENTS.md via /gsd-new-milestone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds ChaCha20-Poly1305 as a new EncryptionAlgorithm alongside AES-GCM.
Refactors AES-GCM into shared AEAD helpers (EncryptAead/DecryptAead)
so both ciphers share PBKDF2 key derivation and wire format
(salt|iv|cipher|tag). Marks TripleDES [Obsolete] as it is no longer
considered safe. ChaCha20-Poly1305 is flagged as not FIPS-compliant.
…ssphrase for PgpGenerateKeyPair

Adds a KeySize property (RsaKeySize enum, default 4096) so users can
choose 2048/3072/4096 instead of being locked to the previous
hardcoded value. Also adds a PassphraseSecureString input alongside
the existing string Passphrase, with a paired PassphraseInputModeSwitch
(marked Obsolete; the design-time menu action drives the toggle) and
CacheMetadata validation that the active input is set. Moves
ContinueOnError to the Options category to match other activities.
…ategory cleanup across activities

Adds InArgument<IResource>/InArgument<ILocalResource> properties for key
and file inputs across Encrypt/Decrypt File/Text and PGP activities so
users can point at Orchestrator storage buckets instead of plain file
paths; resolution is done lazily via ToLocalResource().ResolveAsync().

Marks the legacy *InputModeSwitch properties [Obsolete] (the design-time
Main menu actions now drive these toggles) and adds matching CS0618
pragmas in tests that still exercise the legacy flags.

Moves ContinueOnError from the Common category to Options across all
affected activities for consistency. Updates ActivitiesMetadata.json,
resx strings, and Designer.cs to register the new properties and
localized labels.

Also pulls in the new Shift-JIS encoding test and ChaCha20-Poly1305
round-trip/cross-cipher tests, and adopts the new
UiPath.Shared.ViewModels.Tests shared project (with ActivityMetadataTests
and a MetadataChecker harness) registered in the solution files.
…crypt + document wire format (STUD-64429)

Customer report STUD-64429: TripleDES (and by extension every other
non-AEAD symmetric algorithm in this package) produces ciphertext that
no external tool can decrypt, and rejects ciphertext from external
tools, because the UiPath wire format Base64(salt | IV | ciphertext)
with PBKDF2-derived keys is undocumented and the decrypt failure
surfaces as a generic CryptographicException.

This change closes the documentation + diagnostic gap without altering
the encryption pipeline. CryptographyHelper.DecryptData now:

- Rejects inputs shorter than salt + IV with a localized message that
  names the expected wire format and minimum byte count.
- Catches the inner CryptographicException raised by CryptoStream when
  PKCS7 padding fails on a wrong-format / wrong-key input, and rethrows
  it with a hint pointing at the wire-format spec and likely causes
  (different tool / wrong key / wrong encoding).

Adds a docs/symmetric-wire-format.md reference covering the exact byte
layout per algorithm, PBKDF2 parameters, and copy-paste Python encoder
and decoder snippets for both non-AEAD and AEAD ciphers. Appends a
short wire-format note to the Algorithm description on EncryptText,
DecryptText, EncryptFile, DecryptFile.

Adds four tests covering: the salt-randomization contract across
TripleDES / AES / DES, activity-level non-determinism on repeat
encryption, the new too-short diagnostic, and the new wrapped padding
diagnostic with InnerException preserved.

500/500 existing tests continue to pass.
…rId, align with OpenPGP convention

PgpGenerateKeyPair previously exposed the OpenPGP User ID as a property
called Username with the description "username (or email) to associate
with the generated key pair". This undersells what the field is: the
OpenPGP User ID packet (RFC 4880 / RFC 9580 §5.11) embedded in the
generated public key, conventionally an RFC 2822 mailbox like
"Name <email@example.com>".

Renames the C# property and the resx keys from Username to UserId. The
displayed label becomes "User ID", the description points users at the
mailbox form with an example, and the input shows a placeholder hint
("Your Name <your.email@example.com>") via EditPlaceholder. Description
also names the public key specifically — the User ID is what others see
when importing it.

Reorders the design-time layout so each field sits with the key it
belongs to:

  PublicKeyFilePath → UserId → PrivateKeyFilePath → Passphrase

This pairs UserId with the public key (where the User ID packet lives)
and keeps Passphrase adjacent to PrivateKeyFilePath (matching every
other PGP activity in the package).

Property rename is safe because PgpGenerateKeyPair has not yet been
publicly released with this property — no XAML in customer workflows
binds the old Username name.

Localized resx files keep the old _Username_* keys with their old
translations; the resource manager falls back to English on the new
_UserId_* keys until a translation pass picks them up. Same pattern as
the recent storage-resource commit.

500/500 existing tests continue to pass.
…PGP-passphrase-bearing activities

Mirrors the PgpGenerateKeyPair pattern (commit b4c4c86) across the six
activities that consume a PGP passphrase:

- EncryptText, DecryptText, EncryptFile, DecryptFile (when Algorithm == PGP)
- PgpSignFile, PgpClearSignFile (unconditional)

Each activity now exposes Passphrase as InArgument<string> alongside a
new PassphraseSecureString as InArgument<SecureString>, with a hidden
PassphraseInputModeSwitch (KeyInputMode, [Browsable(false)] +
[Obsolete]) that the design-time Main-menu toggle drives. The previous
InArgument<SecureString> Passphrase contract is renamed to
PassphraseSecureString and Passphrase becomes the new string-typed
property — confirmed acceptable because this typing had not been
publicly released.

CacheMetadata validates whichever side is null based on the active
mode (gated on Algorithm == PGP for the four symmetric activities and
on SignData == true for the encrypt-side variants). Execute() reads
the active side, throws ArgumentNullException with the matching
resource key, and collapses SecureString to string via NetworkCredential
before calling the helpers. PgpStreamHelper and PgpFileSignHelper
signatures were updated to take a plain string passphrase since the
activity now performs the conversion (drops System.Net / System.Security
imports from both helpers).

ViewModel bases (EncryptCryptoViewModelBase, DecryptCryptoViewModelBase,
PgpSignViewModelBase) gained ConfigurePassphraseInputModeMenuActions,
SwitchToPassphrase / SwitchToPassphraseSecureString task handlers, and
persistence fields. EncryptCrypto and DecryptCrypto bases route final
visibility through a shared ApplyPassphraseVisibility() that combines
Algorithm == PGP (and SignData on the encrypt side) with the active
mode flag. Concrete viewmodels call ConfigurePassphraseInputModeMenuActions()
in InitializeModel.

Resx adds three keys per activity (PassphraseSecureString Name/Description
and PassphraseInputModeSwitch Name/Description, with the existing
Passphrase Name/Description re-described for the plain-string variant).
Designer.cs got 24 matching accessors. Localized resx files left alone
— new keys fall back to English.

ActivitiesMetadata.json adds a PassphraseSecureString block per
activity mirroring the existing Passphrase block's flags. The
[Browsable(false)] PassphraseInputModeSwitch follows package
convention and is not added to metadata.

Tests in PgpTests.cs migrate the two existing
"new InArgument<SecureString>" passphrase usages to
"new InArgument<string>(Passphrase)" (using the PgpTestBase const),
and a new test PgpDecryptText_WithSecureStringPassphrase_RoundTrips
exercises the SecureString branch end-to-end.

Docs (6 .md files) gain a PassphraseSecureString row alongside the
updated Passphrase row, with a note about the Main-menu toggle.

521 of 521 tests pass (500 baseline + 21 newer additions including the
new SecureString round-trip).
…ry in Encrypt/DecryptFile

Before: setting OutputFileName on EncryptFile or DecryptFile while
leaving OutputFilePath empty produced an empty filePath in
FilePathHelpers.GetDefaultFileNameAndLocation. That empty string flowed
through to CryptographyLocalItem's constructor and File.WriteAllBytes("")
threw ArgumentException("Empty path name is not legal"). The intent of
OutputFileName-only — "rename the output, keep the directory" — was
silently unreachable.

After: when OutputFileName is provided and OutputFilePath is empty, the
helper now builds filePath by combining the input file's directory with
the given OutputFileName, and applies the same File.Exists / Overwrite
guard the all-defaults branch already does. Activities that supplied
both OutputFilePath and OutputFileName, or only OutputFilePath, are
unaffected.

Adds a regression test EncryptFile_WithOnlyOutputFileName_WritesToInputDirectoryWithThatName
that asserts the produced output is at <input-dir>/<custom-name> and
non-empty. The earlier 521 tests continue to pass; total now 522.
…ocument OutputFilePath fallback

- Sentence case for output display names: `Encrypted File`/`Decrypted File`/`Encrypted Text`/`Decrypted Text` -> `Encrypted file`/etc.
- `OutputFilePath` description on EncryptFile/DecryptFile now mentions the `<input-name>_Encrypted<ext>` / `_Decrypted<ext>` fallback when the field is left empty.

English resx only; the 12 locale resx files continue to fall back to English (same pattern as other recent additions on this branch).
…ta on PgpSign/ClearSign/Verify activities

`PgpSignFile`, `PgpClearSignFile`, and `PgpVerify` each declared two distinct `[OverloadGroup]`s for their path/resource pairs (e.g. `InputFilePath` and `PrivateKeyFilePath`). Per WF semantics, distinct groups model *alternative* configurations and only one may be configured at a time -- so any valid customer XAML that set both the input file and the key file triggered:

  "The following overload groups are configured: InputFilePath, PrivateKeyFilePath. Only one overload group should have its arguments configured."

The path/resource pairs are independent inputs (both required), not alternatives, so OverloadGroup is the wrong tool. Removed `[OverloadGroup]` and `[RequiredArgument]` from the four file properties on each activity, and replaced them with a `CacheMetadata` override that validates each pair against its `*InputModeSwitch` (and `Mode` on `PgpVerify`). Closes open item #4 from the session-state notes -- the async PGP-sign activities now have design-time validation that matches the symmetric activities' behavior.

Tests:
- Added six regression tests covering string-path config and sign/verify round-trips for all three activities.
- Added `PgpFullPipeline_Activities_EndToEnd_Works` end-to-end test mirroring a customer-reported workflow (generate -> sign -> clear-sign -> verify signature / clear-signature / public-key).
- Removed the previously disabled-stub comments that were blocked on this bug.
…ecryptFile sign+verify round-trips

The existing PGP file/text tests skipped `SignData=true` / `VerifySignature=true`. Added two round-trip tests that mirror customer workflows passing both public + private keys plus the passphrase through Encrypt/DecryptText and Encrypt/DecryptFile.
…PgpSign/ClearSign/Verify

Direct invocations of `ActivityValidationServices.Validate` prove the new `CacheMetadata` overrides actually run at design-time validation (not just at runtime via `WorkflowInvoker.Invoke`), and that `PgpVerify` in `Mode=PublicKey` correctly does not require the signed-file input while still requiring the public key.
…l activities

Every paired property (path string ↔ IResource, plain string ↔ SecureString) now follows the same pattern that EncryptText.PublicKeyFilePath ↔ PublicKeyFile already used: no dedicated `*InputModeSwitch` enum, viewmodel uses pure `HasValue`-inference for initial mode, and runtime resolves via string-first fallback.

Switch enums disposition (per origin/masters/Cryptography baseline):
- Kept as [Obsolete][Browsable(false)] (still on masters, preserves XAML back-compat): `KeyInputModeSwitch` on Encrypt/Decrypt/Hash and `FileInputModeSwitch` on Encrypt/Decrypt. Still declared but no longer read by viewmodel or runtime.
- Deleted (only added on this branch): every `PassphraseInputModeSwitch`, every `*InputModeSwitch` on the PGP activities, and `FileInputModeSwitch` on KeyedHashFile.

Runtime fallback shape:
- File pairs: try the string `*FilePath` first; if empty, resolve the `*File` IResource to a local path. Throws ArgumentNullException if both are empty.
- Key/passphrase pairs: try the plain `Key`/`Passphrase` string first; fall back to the SecureString side; throws if both empty.

Extracted `Helpers/PgpFileResolver.cs` to consolidate the path-resolution and passphrase-resolution helpers used by PgpSignFile / PgpClearSignFile / PgpVerify.

Renamed `PgpGenerateKeyPair` → `PgpGenerateKeys` (class, viewmodel, helper method, resx keys, metadata, docs file) to match the display name "PGP Generate Keys". Unpublished activity, so no back-compat shim. The other three PGP activities (`PgpSignFile`, `PgpClearSignFile`, `PgpVerify`) already matched their display names.

Tests:
- Dropped the design-time `*_CacheMetadata_*` tests for PGP activities — runtime fallback is the source of truth now, no design-time validation for these pairs.
- Renamed `*_FilePathMode_*_ThrowsAtRuntime` to `*_Empty*_Throws` since the switch is gone; behavior identical.
- Trimmed `*_Has_IResource_Properties` reflection tests to the two pair sides (no more switch property to check).
- All 9 tests that previously set `*InputModeSwitch` properties drop those lines for deleted switches; kept-obsolete ones still build fine because the property still exists.

Net: 518 / 518 tests passing.
Matches the spelling used by GnuPG (`--clearsign` / `--clear-sign`) and the OpenPGP RFC 9580 ("cleartext signature"). Unpublished activity, so no back-compat shim. The other PGP activities (`PgpSignFile`, `PgpVerify`, `PgpGenerateKeys`) already match their display names.

Renamed surfaces:
- Activity class, viewmodel class, files, resource keys, ActivitiesMetadata.json entries, docs file.
- Display name "PGP Clear Sign File" → "PGP Clearsign File".
- PgpVerify Mode dropdown label "Clear-Signed File (Text)" → "Clearsigned File (Text)" (and matching tooltips).
- Output property display "Clear-Signed File" → "Clearsigned File".
- Misc display strings ("clear signing" → "clearsigning", "clear-signature" → "clearsignature").

Not renamed (internal identifiers, kept for code stability):
- `CryptographyHelper.PgpClearSign(...)` helper method.
- `PgpVerifyMode.ClearSignature` enum value.
- `ClearSignedFile` output property name on `PgpClearsignFile`.
- Private `_ClearSigned` filename suffix.
PGP runs through PgpCore + BouncyCastle (the open-source edition, not BC-FIPS), which is not a CMVP-validated cryptographic module. The algorithms we configure (RSA, SHA-256, AES-256) are FIPS-approved choices, but FIPS 140 validates the module, not the algorithm — so this code path is not FIPS-compliant.

No behavior change in the Encrypt/Decrypt activities: their CacheMetadata short-circuits the IsFipsCompliant check when `Algorithm == PGP` before it reaches this branch. The correction is for any future caller that might query this directly.
…ackageIconUrl

Silences NU5125 and NU5048 warnings emitted by NuGet on every Cryptography packaging build.

- `<PackageLicenseUrl>` (deprecated) → `<PackageLicenseFile>LICENSE.md</PackageLicenseFile>` pointing at the existing license file two directories up; added `<None Include="..\LICENSE.md" Pack="true" PackagePath="" />` so it ships in the .nupkg.
- The old docs URL that was misused as a license URL moves to `<PackageProjectUrl>` where a docs link belongs.
- `<PackageIconUrl>` (deprecated) removed; no local icon file exists to feed `<PackageIcon>`, and modern NuGet was silently ignoring the URL anyway.
…and design-time warning

Follows up on `85a6006` (which corrected `IsFipsCompliant(PGP)` to return `false`) to make the non-FIPS classification visible in Studio:

- Algorithm dropdown label `PGP` reworded from `PGP (Pretty Good Privacy)` → `PGP - Pretty Good Privacy (Non-FIPS)`. The `(Non-FIPS)` suffix matches the convention used by `ChaCha20-Poly1305 (Non-FIPS)`, `RC2 (Non-FIPS)`, `Rijndael (Non-FIPS)`; the hyphen replaces the redundant inner parens.
- `CacheMetadata` in EncryptFile / DecryptFile / EncryptText / DecryptText: dropped the `Algorithm != EncryptionAlgorithm.PGP &&` short-circuit before the FIPS check. The check now honors `IsFipsCompliant` uniformly, so selecting PGP surfaces the same yellow `FipsComplianceWarning` as RC2 / Rijndael / ChaCha20-Poly1305 do.

The existing warning text ("The selected algorithm does not have a FIPS compliant implementation. This might not work on some machines.") applies equally to the PgpCore + BouncyCastle path — no string changes needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant