Skip to content

It should be clearer why Guid.NewGuid fails in a .NET 7 wasmconsole (because crypto is not present in NodeJS by default) #79683

@eduard-dumitru

Description

@eduard-dumitru

Description

Looks like the wasm-experimental workload doesn't allow for the successful invocation of Guid.NewGuid, when creating a wasmconsole project and running in NodeJS:

image

As it turns out, this is because crypto is not present in the global scope, and Math.random is considered unfit for cryptography.

NodeJS does not provide crypto out of the box, as is the case with browsers.
Yet the wasm-tools and wasm-experimental workloads do not guide the user programmer in such a way that they're aware of this conundrum while employing dotnet new wasmconsole and then going ahead and calling Guid.NewGuid.

Given how Guid.NewGuid can be called by any 3rd party lib, far out in the dependency tree, I think a better error message would be in order, if technically possible.

@SteveSandersonMS 's dotnet-wasi-sdk was able to quickly and effectively guide me toward implementing part of WASI_SNAPSHOT_PREVIEW1 (including this very crypto thing).

It even told me what extern InternalCalls lacked bindings (i.e., TaskQueue.SetTimeout) so I quickly backfilled those through c and js.

Reproduction Steps

dotnet new wasmconsole
  • In Program.cs call Guid.NewGuid:
Console.WriteLine($"The guide is {Guid.NewGuid()}");
return 0;
  • Build and run
dotnet build
dotnet run

Expected behavior

I would expect Guid.NewGuid to work.
Given the workloads are experimental, I don't expect everything to work, but I think it's essential for you guys to receive feedback.

Also, I would suspect other capabilities to be lacking, such as TimerQueue.SetTimeout and whatnot, but those seem to work.

Actual behavior

You should be able to create a Guid in .NET 7 when targeting wasm and NodeJS by using a provided workload.

Regression?

I don't think this is a regression.

Known Workarounds

A mischievous workaround is falsifying the cryptographical strength of Math.random and adding right at the beginning of main.mjs:

globalThis.crypto = {
    getRandomValues(array) {
        for (let i = 0; i < array.length; i++) {
            const value = Math.floor(Math.random() * 256);
            array.set([value], i);
        }
    }
}

A better workaround is to turn the whole thing into a module project and install crypto.

Configuration

  • The problem is particular to the configuration, but not to the OS.
  • I am not using Blazor, but I am using .NET 7 compiled to wasm and running in NodeJS.

dotnet workload list

Installed Workload Id      Manifest Version      Installation Source
--------------------------------------------------------------------
wasm-experimental          7.0.1/7.0.100         SDK 7.0.100
wasm-tools                 7.0.1/7.0.100         SDK 7.0.100

dotnet --list-sdks

7.0.101 [C:\Program Files\dotnet\sdk]

Fwiw:

dotnet --list-runtimes

Microsoft.AspNetCore.App 7.0.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 7.0.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 7.0.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

node --version

v18.12.1

systeminfo

...
OS Name:                   Microsoft Windows 11 Enterprise
OS Version:                10.0.22000 N/A Build 22000
...

Other information

The call chain:

  • when calling Guid.NewGuid
  • it calls Sys.Interop.GetCryptographicallySecureRandom
  • which calls libSystem.Native/SystemNative_GetCryptographicallySecureRandomBytes
  • which calls the js-implemented extern called dotnet_browser_entropy
  • which returns non-zero if crypto is nowhere in sight

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions