dotnet / fsharp

The F# compiler, F# core library, F# language service, and F# tooling integration for Visual Studio
https://dotnet.microsoft.com/languages/fsharp
MIT License
3.83k stars 773 forks source link

Impossible viable fork #17184

Closed Adahel closed 1 month ago

Adahel commented 1 month ago

Is your feature request related to a problem? Please describe.

I'm going to write a project that needs to use .NET Framework 4, which has to be 100% strong name and that needs the features of at least version 4.7 of F#. I was able to make a working fork of the latest FSharp.Core, but I can't use any closed third-party libraries, because Microsoft pseudo opened F#, but didn't open MSFT.snk like they opened Open.snk for other .NET projects. I also can't sign with my own SNK and redirect public key token from a third-party assembly because this function never existed and my goal is to backport. Late signature and public signature cannot be tolerated in my project.

My issue is simple, I can modify many strongly named libraries with Open.snk in .Net Core, but why can't I modify F#?

Describe the solution you'd like

The solution that Microsoft provides.

Describe alternatives you've considered

Factor out the prime number of MSFT.snk which is currently RSA SHA-1, using the cado-nfs program, to make F# truly open source. Since my goal is to backport any future changes to RSA SHA-2, this is irrelevant to me.

Additional context

Conversation on Microsoft Copilot: https://sl.bing.net/he65gwPYzfM

Adahel commented 1 month ago

Please reopen with specific question.

Now there's a question.

vzarytovskii commented 1 month ago

Microsoft pseudo opened F#, but didn't open MSFT.snk like they opened Open.snk for other .NET projects. ... My issue is simple, I can modify many strongly named libraries with Open.snk in .Net Core, but why can't I modify F#?

I honestly have no idea what Open.snk is. What are the projects it's used in?

vzarytovskii commented 1 month ago

Describe the solution you'd like

The solution that Microsoft provides.

Solution to what? what's the exact need? To patch a shipped dll and re-sign it? Why is it needed? We usually ship FSharp.Core backwards compatible, so, if functionality is missing from older version, a new one can likely just be taken as is. Or is the issue elsewhere? Other libraries? Why can't just additional library be shipped and loaded?

Or is that you want to "rebuild" FSharp.Core for .NET4.0, which we don't ship?

abelbraaksma commented 1 month ago

You can just use the older libs of F# Core, right? They're publicly available.

Why would you want to try to break encryption? What possible gain can you have for that, other than breaking security and the law? Certainly, you're not suggesting that MS release their private keys?

What do you mean with "it's not truly open source"? Everything is in this repo, literally.

Adahel commented 1 month ago

Microsoft pseudo opened F#, but didn't open MSFT.snk like they opened Open.snk for other .NET projects. ... My issue is simple, I can modify many strongly named libraries with Open.snk in .Net Core, but why can't I modify F#?

I honestly have no idea what Open.snk is. What are the projects it's used in?

Sorry, my computer crashed and I had to restart. I don't know what you call Open.snk internally, but several .Net Core projects, but not all, use this complete and open snk. Here is a bad example: https://github.com/dotnet/LLVMSharp/blob/main/Open.snk

Open.snk has PublicKeyToken=cc7b13ffcd2ddd51, if you open all .NET assembly in ILSpy, you will discover that there are many, but not all.

Describe the solution you'd like The solution that Microsoft provides.

Solution to what? what's the exact need? To patch a shipped dll and re-sign it? Why is it needed? We usually ship FSharp.Core backwards compatible, so, if functionality is missing from older version, a new one can likely just be taken as is. Or is the issue elsewhere? Other libraries? Why can't just additional library be shipped and loaded?

Or is that you want to "rebuild" FSharp.Core for .NET4.0, which we don't ship?

FSharp.Core above 4.1.18 does not load in .Net Framework 4.0 because it has methods that were only implemented in .Net Framework 4.5 and if I add a library like Theraot.core that implements these methods, I can compile, but it does not load after being compiled, as FSharp.Core will always load first before any third-party library, meaning the fork is necessary. https://github.com/theraot/Theraot/issues/121

Adahel commented 1 month ago

You can just use the older libs of F# Core, right? They're publicly available.

This to me is called Tivoization and to me, it is not open source regardless of what the license says and I don't want to use old, limited versions of F#.

Why would you want to try to break encryption? What possible gain can you have for that, other than breaking security and the law? Certainly, you're not suggesting that MS release their private keys?

If you read the conversation on Microsoft Copilot, you will know that the law has loopholes for what I want to do. FSharp is not even distributed in binary with an anti-reverse engineering license like VSCode, so a path has been opened for this possibility. I presented a problem above that can be solved without releasing the private keys. I hope the problem is resolved peacefully.

What do you mean with "it's not truly open source"? Everything is in this repo, literally.

SNK=Tivoization. If I don't have freedom, it's not open source.

vzarytovskii commented 1 month ago

I don't know what you call Open.snk internally, but several .Net Core projects, but not all, use this complete and open snk. Here is a bad example: https://github.com/dotnet/LLVMSharp/blob/main/Open.snk

We don't use it internally in F#, hence I don't really know what's that. Maybe @jaredpar or @baronfel know how do they (or anyone else) handle resigning for shipped assemblies, and whether it's generally supported or not. It sounds to me a bit dangerous to allow users to re-sign the dll after modification with the same key.

vzarytovskii commented 1 month ago

This to me is called Tivoization and to me, it is not open source regardless of what the license says and I don't want to use old, limited versions of F#.

Current source is freely available and buildable, you can surely make changes, build it for desired TFM, and sign it with your key, why is original key needed? Also, shouldn't source fork be better in every sense? Since you will be able to sync any changes we make to it, and have a bunch of improvements.

Adahel commented 1 month ago

This to me is called Tivoization and to me, it is not open source regardless of what the license says and I don't want to use old, limited versions of F#.

Current source is freely available and buildable, you can surely make changes, build it for desired TFM, and sign it with your key, why is original key needed? Also, shouldn't source fork be better in every sense? Since you will be able to sync any changes we make to it, and have a bunch of improvements.

Sorry if I didn't make it clear. I have nothing against security, trust and integrity, but without the original snk I can't use third-party closed libraries linked to any FSharp.Core. I can modify the IL Code of these libraries and sign it with my signature, but I will be violating the copyright of the original library and I cannot contact the original author because I am working with legacy content.

abelbraaksma commented 1 month ago

In that 4yo discussion, you're mentioning you need to support Windows XP. As you're probably aware, Windows XP is no longer supported by Microsoft. Anybody still using it should upgrade. The same is true for Framework 4.5.

You can upgrade to the latest framework, which is NetStandard 2.0 compatible. Since F# Core lib is NS20/21, it's compatible with that. It can be loaded in XP if you must.

You also mention that you don't like the load order of the assemblies. There are some (rather complex) tricks you can play on the load order, but you'll need to dive deep into how Framework loads dependencies. Use FusionLogvw. The assumption in the reference thread that "the language" requires F# Core to be loaded first is wrong. It has nothing to do with that, but purely with the JITter loading whatever types it sees first.

If you want to force another lib to be loaded first or before, ensure that F# Core is not in the GAC. Then, hook to the AssemblyLoad events. Do this in your ModuleInit.

None of these are simple and I'd advice against it (been there, done that, bad idea). Just upgrade and you'll have a much easier time.

And ditch your private build of F#, you don't need it from what I read in that old thread (but of course, you can sign it any way you want if you build it yourself, no need to hack anything).

Adahel commented 1 month ago

In that 4yo discussion, you're mentioning you need to support Windows XP. As you're probably aware, Windows XP is no longer supported by Microsoft. Anybody still using it should upgrade. The same is true for Framework 4.5.

You can upgrade to the latest framework, which is NetStandard 2.0 compatible. Since F# Core lib is NS20/21, it's compatible with that. It can be loaded in XP if you must.

You also mention that you don't like the load order of the assemblies. There are some (rather complex) tricks you can play on the load order, but you'll need to dive deep into how Framework loads dependencies. Use FusionLogvw. The assumption in the reference thread that "the language" requires F# Core to be loaded first is wrong. It has nothing to do with that, but purely with the JITter loading whatever types it sees first.

If you want to force another lib to be loaded first or before, ensure that F# Core is not in the GAC. Then, hook to the AssemblyLoad events. Do this in your ModuleInit.

None of these are simple and I'd advice against it (been there, done that, bad idea). Just upgrade and you'll have a much easier time.

And ditch your private build of F#, you don't need it from what I read in that old thread (but of course, you can sign it any way you want if you build it yourself, no need to hack anything).

I'm going to study what you said to me and I'm wondering how I'm going to do this in a program that runs in F#? Maybe I have to write a wrapper in C#? And it seems like there are possibilities beyond my control. If the user has FSharp.Core deployed in the GAC, will it be impossible to run the program? It seems like a not-at-all-safe workaround, but it was the most useful comment I've read so far. Thank you very much.

I'll leave it to the team to decide whether to leave this issue open or close it. I don't know if I'll have a better solution than @abelbraaksma informed me; this is what the team will decide. Not only that, but I'm happy with the renewed hope I received for my project; after all, I'm not the owner of the truth.

jaredpar commented 1 month ago

Maybe @jaredpar or @baronfel know how do they (or anyone else) handle resigning for shipped assemblies, and whether it's generally supported or not. It sounds to me a bit dangerous to allow users to re-sign the dll after modification with the same key.

Strong name signing has really nothing to do with security. That was the intent originally but is no longer the case. For .NET Framework the strong name only serves to provide identity information for the assembly. Thee public key is part of the assembly and that factors into assembly loading, binding redirects, etc ... . For .NET Core it's essentially ignored.

Unfortunately a lot of the .NET core ecosystem was signed with a strong name key that we can't release publicly (it is one of the few that actually factors into security). At the same time we also can't use a new strong name key because that would change the identity of the assembly (which would be an enormous compat break). For those we just use public signing which works in the vast majority of .NET Framework scenarios.

abelbraaksma commented 1 month ago

I'm going to do this in a program that runs in F#?

Typically, a program does not "run in F#". It runs in .NET, or more technically correct: the .NET runtime loads your libraries and dependencies and starts just-in-time compilation, and executes whatever you have as a main entry point.

If you were to change the public key (which you can, of course, just sign it with something you want), in .NET Framework, the libs won't load the original F# anymore (as suggested by @jaredpar above), but only yours. There's nothing that stops you from doing so, but the obvious drawback is that your library cannot be loaded anymore with other F# Core libraries, as there's going to be a clear conflict.

If you want your libs to be consumed by anybody other than yourself, there's only one way forward: use the libraries as they've been made available from RTM releases.

If the user has FSharp.Core deployed in the GAC, will it be impossible to run the program?

That's up to you. If you need to stick with old technology and mix it with new technology, you will keep running into issue like these. Best you can do is write a launch-program that executes before yours and just cleans up the system properly.

It seems like a not-at-all-safe workaround, but it was the most useful comment I've read so far. Thank you very much.

Nothing of this is safe, as you are trying to use unsupported versions of libraries that are hacked to work with unsafe operating systems. My guess is, this is going to be the least of your worries. But it's possible, it just requires quite a bit of research into the whole of Framework's assembly loading stuff.

KevinRansom commented 1 month ago

As discussed above, Windows XP is not a supported scenario. Signing with full Microsoft key is not a supported OSS scenario. We have never specified the Ecma key as a substitute for the Microsoft key in F# core. It may be that was an error we made back in the day, but Mono did fine without it for years.

I'm going to close this issue won't fix.