Skip to content

build(ffi): move to the upstream diplomat .NET backend - #518

Closed
irvingouj@Devolutions (irvingoujAtDevolution) wants to merge 1 commit into
masterfrom
ffi/diplomat-dotnet-backend
Closed

irvingouj@Devolutions (irvingoujAtDevolution) wants to merge 1 commit into
masterfrom
ffi/diplomat-dotnet-backend

Conversation

@irvingoujAtDevolution

Copy link
Copy Markdown
Contributor

Draft — the diplomat side isn't merged yet.

Gets us off the old CBenoit diplomat fork and onto the rewritten .NET backend. Pinned to my rust-diplomat/diplomat#1226 (fixed-width bool tag) because without it every Result/Option return throws MarshalDirectiveException on .NET Framework — bool isn't blittable, so P/Invoke can't return the struct by value. Once that lands upstream I'll repoint the rev at rust-diplomat and drop the fork.

Rust side had to move with the new backend: DiplomatWriteable → DiplomatWrite, the bridge macro derives Clone/Copy on enums now, mutable opaques need opaque_mut, Result errors must be diplomat types (Pkcs12Error → Box<PickyError>), and box-in-input is gone.

⚠️ The generated C# changes shape, so this is breaking for consumers: setters are methods (builder.Kid = x → builder.SetKid(x)) and enum variants got re-cased (RS512 → Rs512, SHA2_256 → Sha2256).

Still to do: Devolutions.Picky.Tests doesn't compile against the new names (56 errors, all mechanical renames). Didn't want to bury it in this diff — happy to do it here or as a follow-up, tell me which.

Tests: new Devolutions.Picky.Net48Tests runs the bindings on net48 and net10, 14/14 on both. Covers every marshalling shape — object/void/primitive/string/option Results, bool in and out, byte[] returns, iterators, error → exception.

Swaps the old CBenoit diplomat fork for the rewritten .NET backend, pinned to
the blittable-bool fix (rust-diplomat/diplomat#1226) so Result and Option
still marshal on .NET Framework.

Rust side: DiplomatWriteable is now DiplomatWrite, the bridge macro derives
Clone/Copy on enums itself, mutable opaques need opaque_mut, Result errors
have to be diplomat types (Pkcs12Error -> Box<PickyError>), and box-in-input
is gone (Option<Box<RsString>> -> Option<&RsString>).

The generated C# API changes shape: setters are methods (Kid = x becomes
SetKid(x)) and enum variants are re-cased (RS512 -> Rs512). Devolutions.Picky
itself builds clean; Devolutions.Picky.Tests still needs porting to the new
names.

Adds Devolutions.Picky.Net48Tests, which runs the bindings on .NET Framework
4.8 and on modern .NET so the marshalling stays proven on both.
irvingouj@Devolutions (irvingoujAtDevolution) added a commit that referenced this pull request Oct 17, 2026
Moves the .NET bindings off the old
[CBenoit/diplomat@f4d93d57](CBenoit/diplomat@f4d93d5)
fork onto Diplomat's HIR .NET backend, pinned to the protected tag
[`0.16.1-devo.2`](https://github.com/Devolutions/diplomat/tree/0.16.1-devo.2)
(same commit as upstream main,
[002db80c](rust-diplomat/diplomat@002db80)).
crates.io 0.16.1 predates the .NET lifetime rework, so it's a tag until
upstream publishes. Supersedes #518.

Split into a stack so this one is just the migration:
1. #561 drops .NET Framework, targets net8.0
2. #537 `program_name` double free fix
3. #538 getter/setter attrs, no generated diff
4. #569 `manually_disposable` on the 14 types that need it, no generated
diff
5. this PR
6. #541 fixes a use-after-free in `AuthenticodeValidator`
7. #542 wipes key bytes and passphrases on free

The bridge diff here is only what HIR needs: `opaque_mut`, named
lifetimes, `DiplomatWrite`, and no `#[derive(Clone, Copy)]` on enums
(the macro adds it now). The rest is `Generated/`, the addons and test
renames. C# properties stay the same (176 before, 176 after).

### Breaking (C#)
- `GetX()`/`SetX()` methods that duplicated properties are gone; the
properties stay
- `HashAlgorithm`/`ShaVariant`/`JwsAlg`/`SshCertKeyType` members are
re-cased (`SHA2_256` → `Sha2256`), same numeric values
- `DiplomatWriteable`, `AsFFI()`, the raw ctors and `Raw.*` aren't
public anymore; `PickyError.ToDisplay()` returns `string`
- only the 14 types from #569 stay `IDisposable`, the other 84 lose it,
so `using`/`Dispose()` on them won't compile anymore

I grepped the RDM monorepo (RDM, DVLS, Hub, PowerShell): none of the
removed methods or enum names are used. They do dispose `Cert` and
`using` `Pkcs12ParsingParams`/`Pkcs12Attribute`, those lines have to go
when they bump. That's a text search, not a compile.

### Where to look hardest
- the addons take a shared lease for the whole native call, same as the
generated code:
[Pem](https://github.com/Devolutions/picky-rs/blob/d44f8bf021013f285fbd360defe1fb1f964d64b4/ffi/dotnet/Devolutions.Picky/src/Pem.Addons.cs),
[Pfx](https://github.com/Devolutions/picky-rs/blob/d44f8bf021013f285fbd360defe1fb1f964d64b4/ffi/dotnet/Devolutions.Picky/src/Pfx.Addons.cs),
[PublicKey](https://github.com/Devolutions/picky-rs/blob/d44f8bf021013f285fbd360defe1fb1f964d64b4/ffi/dotnet/Devolutions.Picky/src/PublicKey.Addons.cs)
- the test renames in `JwtTests`/`Pkcs7Tests`/`SshTests`, that's what
consumers will have to change

### Tests
- clippy/fmt clean, regenerating the bindings is a no-op (267/267 files)
- `Devolutions.Picky.Tests` 26/26

### Needs a call
- NuGet is date-versioned with no changelog. How do we want to announce
the breaks?

### Not from this PR, spotted while here
- `PuttyPpkEncryptionConfigBuilder` setters call `self.0.clone().x(..)`
and throw the result away, so they do nothing
- `get_argon2_flavour` is a setter with a getter name

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant