Cryptographically secure random numbers in Typescript module

I am writing match logic in a Typescript module.

I need a source of cryptographically secure random bytes. This is a hard requirement subject to future audits.

In Node, I’d use the “crypto” package, but as I understand it we can’t use such modules in Nakama.

So I then wrote a Go module, since there’s “crypto/rand” in the Go module runtime, to give me a specific number of random bytes, only to realise afterwards that there’s no inbuilt way to invoke a Go RPC endpoint from the Typescript runtime, is there? I would have to make a raw HTTP invocations? I don’t really want to make many HTTP calls in my match loop if I can avoid it.

I see that the Nakama runtime object has various crypto functions, like bcrypt and various other hashing functions, and some symmetric encryption functions, but nothing for secure random.

Anyone has a suggestion on the best thing to do here? I have to find a pure Typescript library to do this if such a thing even exists?

Hi @caprica,

You’re correct, it’s possible to mix the various runtimes but it’s not possible to directly invoke functions from each other, as you noted a workaround would be to do a http request via localhost, there’ll be some overhead but the request won’t go through the network, just the network stack.

Alternatively you could implement just the match loop entirely in Go.

Nakama JS runtime uses goja, and it looks like under the hood it uses crypto/rand to power random functions (goja/goja/main.go at af2ceb9156d7feaff65273b8bfde778077fb4b7e · dop251/goja · GitHub), it’s possible that a pure JS crypto library may actually be secure, if the underlying JS function is backed by crypto/rand, but this would require some investigation.

Best.

Thanks for your reply.

I see that the Typescript nkruntime.Nakama object has crypto functions like this (and more) exposed:

/**
 * AES 128 bit block size encrypt
 *
 * @param input - String to encrypt.
 * @param key - Encryption key.
 * @returns cipher text base64 encoded.
 * @throws {TypeError, GoError}
 */
aes128Encrypt(input: string, key: string): string;

/**
 * AES 128 bit block size decrypt
 *
 * @param input - String to decrypt.
 * @param key - Encryption key.
 * @returns clear text.
 * @throws {TypeError, GoError}
 */
aes128Decrypt(input: string, key: string): string;

Presumably these crypto functions have some direct access to crypto/rand somehow?

Would there be any hope in future of getting something similar that wrapped something like crypto.randomBytes() into this runtime module API?

Yes we may consider adding it, but can you provide some more info on your specific use-case? As in, could this be a higher level function or you need random bytes specifically?

If you’d like, you could try to contribute by adding this JS function yourself: nakama/server/runtime_javascript_nakama.go at master · heroiclabs/nakama · GitHub. If you do try to contribute, please consider also adding an equivalent function to the Lua runtime to keep feature parity between the two: nakama/server/runtime_lua_nakama.go at master · heroiclabs/nakama · GitHub.

The use-case is to implement things like secure shuffles. So I need a source of securely random bytes to implement a secure Fisher Yates shuffle on various arrays.

Honestly, I am surprised this has not come up before, I guess most people/games are happy with Math.random(), but that is not really a good source of randomness - PRNG vs CRPRNG. For the type of game I’m writing, I will (eventually) be audited on my use of random number generation and this audit will fail without CSPRNG.

I suppose it’s possible a higher-level secure array shuffle function would be more suitable, without exposing raw random bytes, but I specifically need to implement Fisher Yates for a fair distribution, so this higher level function would need to be “secure random Fisher Yates shuffle”. At least with just exposing random bytes, it would be more flexible for other shuffle algorithms if people needed that.

I am open to any other way to do this if I’ve missed something, but before Nakama I was using Node crypto getRandomBytes so that’s why I’m fixated on it.

I can try looking at a contribution, I have no idea about Lua but I guess it can’t be that difficult. In the meantime I’m making do with invoking my Go module via Nakama HTTP.

Is there some contributor guide that gives all the steps for adding new API?

Writing the code in the two places you listed for Javascript and Lua was straightforward enough, the docker build and test process all works without error, I can get a new nakama binary, but…

I don’t see place to write API unit tests.
I don’t see how to get a new API client with my new API so I could test it that way from my own project.

Never mind, I have something that actually works in both Javascript and Lua modules.

I’ll see about PR’s next.