ektrah / nsec

A modern and easy-to-use cryptographic library for .NET 8+ based on libsodium
https://nsec.rocks
MIT License
374 stars 52 forks source link

Request for updated Nuget Release #73

Closed enclave-alistair closed 1 month ago

enclave-alistair commented 1 year ago

Hi @ektrah, can I inquire as to your plans for the next nuget package release? We're currently maintaining a forked build so we can get your commit for "unmanaged function pointers" (https://github.com/ektrah/nsec/commit/114231a21aa75c5c90ea8e95da2777eeadfffe78), needed for iOS support, as a package.

I know iOS isn't on your official supported list, but we're using NSec quite successfully with a few build/loader tweaks in the iOS app, would be nice to switch over to the regular NSec package.

ektrah commented 1 year ago

I didn't think the changes since the last release were significant enough for a new release, but I've done a new pre-release now. If all looks good, I could do a new release soon.

Having iOS on the official support list would be nice. However, I still haven't been able to fully figure out what is needed for this. Are the changes in 114231a21aa75c5c90ea8e95da2777eeadfffe78 enough or is there anything else that should be done in NSec (or libsodium) to help making it work?

enclave-alistair commented 1 year ago

So, as you may be aware, iOS is a bit of a pain. To make libsodium correctly load and be consumed by NSec, here's what you need in the iOS project.

1. Libsodium build for iOS

You need a built libsodium.a for iOS. This could be avoided if the libsodium nuget shipped an iOS build, which would be just about doable I think with a GitHub macOS build runner.

2. Include the binary in your bundle

Add:

<NativeReference Include="Platforms/iOS/libsodium.a" Kind="Static" ForceLoad="True" />

in the project file (probably in an '$(TargetFramework)'=='net7.0-ios') conditional.

3. Tell the runtime where the libsodium symbols are

The iOS build will statically link all the libsodium symbols directly into the generated binary, but that means there is no longer in a "libsodium" library to DllImport, so we must tell it, using NativeLibrary.SetDllImportResolver():

// As early in your app as possible.
NativeLibrary.SetDllImportResolver(typeof(NSec.Cryptography.Ed25519).Assembly, ResolveLibsodium);

private static IntPtr ResolveLibsodium(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{
    if (libraryName == "libsodium")
    {
        return NativeLibrary.GetMainProgramHandle();
    }

    // fall back to the system resolver.
    return IntPtr.Zero;
}

This is not necessary if the NSec library uses __Internal rather than libsodium for its DllImport attributes (__Internal is a special value that the runtime understands as "this program"), but obviously that only works on iOS.

4. Preserve the libsodium symbols from being trimmed out

Due to point 3, there is now no compile-time connection between the libsodium symbols consumed by the NSec library and the symbols themselves. So, the iOS compiler strips them out, removing all those libsodium functions.

To get around that, we currently duplicate the Interop folder into our app, and change the Interop.Libraries.Libsodium constant to __Internal. These Interop functions are not used, but "trick" the compiler into preserving the symbols.

My instinct tells me that there may be a way to communicate this list of "preserve these symbols please" to the iOS compiler, but I haven't found a better way yet other than duplicating the code as we currently do.

It may be that if the NSec library did similar and included __Internal variants of all it's interop methods that those would propagate and preserve the symbols, but I'm not sure, and that's really janky.

ektrah commented 1 year ago

I've created a branch that changes the Interop.Libraries.Libsodium constant to __Internal on iOS: https://github.com/ektrah/nsec/compare/master...ios. Here is the NuGet package: artifact.zip. Could you check if that helps with 3. and 4.?

Could MSBuild .props and .targets in a package help making 2. more convenient?

Regarding 1., libsodium builds pre-compiled binaries for all Apple targets, but seems not to have included them in the NuGet package yet.

enclave-alistair commented 1 year ago

Don't props and targets in a package only apply to the directly-referencing package? What happens if it's referenced indirectly (as we do)?

If libsodium could build iOS and Android binaries into its nuget package, that would be superb, because we then don't need to maintain their own. I hadn't seen there were apple-target binaries available.

ektrah commented 1 year ago

The linked page talks about buildTransitive (NuGet 5.0+) for assets that flow transitively. I've never tried that yet though.

There has been some great discussion in https://github.com/jedisct1/libsodium/discussions/1235 about MacCatalyst support that also helps with iOS support (PR: https://github.com/jedisct1/libsodium/pull/1238). I think there are still a few last pieces missing, but not much. I don't know about Android.

ektrah commented 11 months ago

@enclave-alistair Any feedback on the ios branch?

davhdavh commented 8 months ago

libsodium 1.0.19 was released, and the current NSec.Cryptography is locked to < 1.0.19

samuel-lucas6 commented 8 months ago

@davhdavh There's a preview release that supports 1.0.19. Even if there wasn't, it's important to remember that 1.0.19 was only released recently and people are busy and work on other things.

ektrah commented 1 month ago

An updated NuGet package including the commit for "unmanaged function pointers" has been released.