Skip to content

Commit ce9c7cf

Browse files
committed
Replace JS snippet with wasi call for random byte generation
1 parent e26657d commit ce9c7cf

File tree

1 file changed

+44
-19
lines changed
  • cardano-wasm/src/Cardano/Wasm/Internal/JavaScript

1 file changed

+44
-19
lines changed
Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,58 @@
1+
{-# LANGUAGE CApiFFI #-}
12
{-# LANGUAGE CPP #-}
23

34
module Cardano.Wasm.Internal.JavaScript.Random (getRandomBytes) where
45

5-
import Data.ByteString (ByteString)
6-
76
#if !defined(wasm32_HOST_ARCH)
87

8+
import Data.ByteString (ByteString)
9+
910
getRandomBytes :: Word -> IO ByteString
1011
getRandomBytes _ = error "getRandomBytes is not implemented for non-WASM targets"
1112

1213
#else
1314

14-
import Cardano.Wasm.Internal.ExceptionHandling (rightOrError)
15-
16-
import Data.ByteString.Base16 qualified as Base16
17-
import Data.ByteString.Char8 qualified as BSC
18-
import GHC.Wasm.Prim
19-
20-
-- | Create a GRPC-web client for the Cardano API.
21-
foreign import javascript safe "{ let numBytes = $1; \
22-
let randomBytes = new Uint8ClampedArray(numBytes); \
23-
crypto.getRandomValues(randomBytes); \
24-
return (randomBytes.reduce((acc, next) => acc.concat(next.toString(16).padStart(2, '0')), '')); \
25-
}"
26-
js_generateRandomBytes :: Int -> IO JSString
27-
15+
import Control.Exception (throwIO)
16+
import Data.ByteString (ByteString, packCStringLen)
17+
import Data.Word (Word8)
18+
import System.IO.Error (mkIOError, userErrorType)
19+
20+
import Foreign.C.Types (CInt (..), CSize (..))
21+
import Foreign.Marshal.Alloc (allocaBytes)
22+
import Foreign.Ptr (Ptr, castPtr)
23+
24+
-- | Import the 'random_get' function from wasi
25+
-- module provided by the WASI runtime.
26+
--
27+
-- It takes a raw pointer to a u8 buffer (Ptr Word8) and the buffer's
28+
-- length (CSize) and returns a CInt representing the error code number.
29+
-- A return value of 0 means success.
30+
foreign import ccall "__wasi_random_get"
31+
wasi_random_get
32+
:: Ptr Word8
33+
-- ^ Pointer to the buffer to fill
34+
-> CSize
35+
-- ^ Number of bytes to write
36+
-> IO CInt
37+
-- ^ Returns 0 on success, or an errno code
38+
39+
-- | A safe Haskell wrapper around 'random_get' function from wasi.
40+
-- It requests the given number of cryptographically secure random
41+
-- bytes and returns them as a ByteString.
2842
getRandomBytes :: Word -> IO ByteString
29-
getRandomBytes n = do
30-
hexRandomBytes <- fromJSString <$> js_generateRandomBytes (fromIntegral n)
31-
rightOrError $ Base16.decode $ BSC.pack hexRandomBytes
43+
getRandomBytes n =
44+
allocaBytes (fromIntegral n) $ \ptr -> do
45+
-- Get pointer to allocated buffer
46+
errno <- wasi_random_get ptr (fromIntegral n)
47+
if errno == 0
48+
-- If successful, pack the bytes from the pointer into a Haskell managed ByteString
49+
then packCStringLen (castPtr ptr, fromIntegral n)
50+
else -- Otherwise, throw we throw
51+
throwIO $
52+
mkIOError
53+
userErrorType
54+
("wasi_random_get failed with errno: " ++ show errno)
55+
Nothing
56+
Nothing
3257

3358
#endif

0 commit comments

Comments
 (0)