Web Assembly in a browser and beyond — Crypto Example

Web Assembly

  • is a low level portable binary format (bytecode) for a stack-based virtual machine

  • is a W3C web standard, that runs in a sandbox, on all major browsers

  • runs nearly as fast as native machine code, and enables high performance applications on the Web

  • language, hardware and platform independent (unlike Java applets and Flash)

  • can be generated from most high level compiled languages — C, C++, Java, Rust, Go, F#, C# etc

  • code is validated and executes in a memory-safe, sandboxed environment preventing data corruption or security breaches

  • memory safety means exploit techniques such as stack smashing and ROP are not possible

  • complements Javascript — for e.g. by enhancing the performance of CPU intensive components of a web app

It is important to reiterate that Wasm is a web standard and therefore part of the Web Platform. This is unlike Java and Flash Applets which were second class citizens on the browser — they used the browser’s plugin functionality and worked within a frame in a web page.

Note: Currently, there is no way to interact with the DOM. This means you cannot do something like element.innerHTML to update a node from WebAssembly.
Instead, you have to go through Javascript to set the value.

A cartoon intro to WebAssembly, a 6 part series of articles by Lin Clark explains WebAssembly very well.

As mentioned earlier, most major languages have compilers that generate Web Assembly. I will mention 2 of them here — Rust and Go.

Rust, from Mozilla, is a fantastic systems programming language, that gives you the performance of C without the nuisance of buffer overflows!

It compiles to machine code, has a fast startup time as there is no runtime[^1].
You get automatic resource management — no need to close sockets or files, and no "use-after-release" bugs.

As there is no runtime overhead of (tracing) garbage collection, Rust is quite suitable for performance critical and embedded applications.

The learning curve for Rust is steeper than languages such as Go and Java, primarily because automatic memory management is achieved based on concepts such as ownership, references and borrowing. One can say that garbage collection happens at ‘compile time’ and for Rust newbies, getting the damn program to compile can be frustrating! But the error messages from the compiler are so good and specific that you can fix them after a few iterations of edit-compile cycle.

Rust’s rich type system and ownership model guarantee memory-safety and thread-safety and enable you to eliminate many classes of bugs at compile-time. Major parts of the Firefox browser are written in Rust.

Rust can be compiled to Web Assembly which loads pretty fast in the browser as Rust doesn’t have a runtime.

Go (from Google) is another statically typed language. Go is very easy to learn.
Writing multi-threaded programs is easy with go-routines.
Go comes standard with lots of Crypto libraries and so it is easy to write programs that use hashing, encryption, certificates, signatures etc. It is a great learning tool and reference as you can look up the implementation of the crypto libraries from your editor (like emacs) or IDEs.

I wanted to write a web app that shows some crypto operations like digital signatures, key exchange using Elliptic curves, some well known vulnerabilities etc etc. Doing Crypto with Javascript will be very slow. To be precise, as I am not a Javascript developer, I would have used a language like Haskell, F# Scala or Java and transpile it to Javascript. However, as all major browsers support Web Assembly and it is supposed to run nearly as fast as native machine code, I decided to write my program in F# and Go which can be compiled to Web Assembly.

The fsharp version may take several seconds to load as the .NET runtime as well as BouncyCastle dlls have to be downloaded. You can run this by visiting https://fsharp-crypto.srinivasan.biz

The Web Assembly (wasm file) generated from Golang (which includes the go-runtime) is almost 4 Megabytes in size but less than 1 MB over the wire as it compresses well. These are early times for Web Assembly and we can expect optimizations to reduce the size of the wasm file significantly. Note that there is a version of Go called tinygo — with it, one can create programs where the wasm file is only a few kilobytes! Needless to say, tinygo has several limitations and so the crypto example I wrote cannot use tinygo.

What you see below is the Web Assembly program that I created using golang. You may have to scroll the window to see the complete app.

[^1]: To be precise, only Assembly language is considered to have no runtime. But for practical purposes, languages like C and Rust are considered to have no runtime.

You can also open the app by visiting https://golang-blog-example.srinivasan.biz in the browser. The app may take a few seconds to load — this is because it has to download the .NET runtime and BouncyCastle crypto dlls. Once loaded, it should run very fast. I plan to add more functionality.

Leave a Reply

Your email address will not be published. Required fields are marked *