This is a wrapper for the S4 encryption library:
S4 is a modern crypto library which focuses on making API's that are easy to use. It can be cross compiled to multiple targets, including to WebAssembly for use in a browser.
To make things easy for developers, a pre-compiled version of S4 is included with the project. The exact commit that was compiled is referenced by commit hash in the filename. See the wasm
folder for more info.
$ npm install s4js
Using WebAssembly means jumping through a few hoops. As this technology becomes more mainstream, the recommended practices will evolve, and become simpler. For now, here's what I recommend:
The S4 library was compiled using emscripten, and outputs 2 files:
- libS4.wasm
- libS4.js
The wasm file is the compiled WebAssembly code. And the libS4.js file was automatically generated by emscripten, and contains the logic to load the wasm file. This logic supports both the browser & Node.js. In other words, you don't have load the wasm file manually. Instead you load the libS4.js file, and it handles loading the wasm for you.
- Place the
libS4.wasm
&libS4.js
files in the same directory.
Now when you load libS4.js it will create an object in the GLOBAL scope named ModuleS4
. If there is already an object in the global scope named ModuleS4
, then libS4.js will use it. This gives us various hooks to discover when the wasm file has been loaded, and is ready for us to use.
Add something like this to your HTML:
<script type="text/javascript">
var onModuleS4Initialized = [];
var ModuleS4 = {
isRuntimeInitialized: false,
onRuntimeInitialized: function() {
console.log("WASM: ModuleS4.onRuntimeInitialized()");
ModuleS4.isRuntimeInitialized = true;
try
{
for (var i = 0; i < onModuleS4Initialized.length; i++)
{
var listener = onModuleS4Initialized[i];
listener();
}
}
catch (e) {
console.error("Exception while attempting to invoke listeners in onModuleS4Initialized array: "+ e);
}
},
print: function(text) {
console.log("WASM [log]: "+ text);
},
printErr: function(text) {
console.error("WASM [err]: "+ text);
}
};
</script>
<script async type="text/javascript" src="%PUBLIC_URL%/libS4.js"></script>
(If using react, you place both libS4.wasm & libS4.js in the public
folder. And you can add the above code to public/index.html
.)
Note that we setup our ModuleS4
variable BEFORE asking the browser to load the libS4.js file. This ensures our hooks will be in place.
Now in your javascript code you can create an s4 instance like so:
import {S4, S4Module} from 's4js';
// If using Typescript, add these for the compiler:
interface ModuleLoader extends S4Module {
isRuntimeInitialized: boolean
}
declare var onModuleS4Initialized: Array<()=>void>;
declare var ModuleS4: ModuleLoader;
// And then add this somewhere to initialize S4
const wasmReady = ()=> {
const s4 = S4.load(ModuleS4);
if (s4 == null)
{
console.log("Failed loading WASM crypto library !");
}
else
{
console.log("WASM crypto library ready");
// Now go do something cool with crypto...
}
}
if (ModuleS4.isRuntimeInitialized) {
wasmReady();
}
else {
console.log("Waiting for WASM crypto library...");
onModuleS4Initialized.push(wasmReady);
}
Once you've got your s4
instance, you're ready to use the crypto lib, with blazing fast WebAssembly performance !
There's example code that demonstrates getting up-and-running in the browser: examples/web/index.html
Due to browser restrictions, you can't simply open the HTML file directly. You'll get the following error:
Fetch API cannot load file:///Users/robbie/Programs/4thA/S4_Javascript/dist.browser/libS4.wasm. URL scheme must be "http" or "https" for CORS request.
So you have to serve the file using a local webserver. The serve
tool is one solution:
$ npm install -g serve
$ cd <to s4js ROOT directory>
$ serve
┌───────────────────────────────────────────────────┐
│ │
│ Serving! │
│ │
│ - Local: http://localhost:5000 │
│ - On Your Network: http://192.168.1.212:5000 │
│ │
│ Copied local address to clipboard! │
│ │
└───────────────────────────────────────────────────┘
Note that you need to run serve from the project ROOT directory. This is because we need to reference files in /dist.browser
.
Finally, run the example in your browser:
http://localhost:5000/examples/web/index.html
Several hashing algorithms are supported:
- MD5
- SHA1
- SHA224
- SHA256
- SHA384
- SHA512
- SKEIN256
- SKEIN512
- SKEIN1024
- SHA512_256
- xxHash32
- xxHash64
- SHA3_224
- SHA3_256
- SHA3_384
- SHA3_512
To hash small amounts of data, the hash_do
function is easy:
import {S4HashAlgorithm} from 's4js';
// inputData: Uint8Array
// returns : Uint8Array
const hashData = s4.hash_do(S4HashAlgorithm.SHA3_256, inputData);
If you want to hash a string, you can convert it to UTF-8:
import {S4HashAlgorithm} from 's4js';
import {TextEncoder} from 'text-encoding-utf-8'
const str = "hash me";
const input = TextEncoder().encode(str);
const hashData = s4.hash_do(S4HashAlgorithm.SHA3_256, inputData);
const hashHexStr = s4.util_hexString(hashData)
To hash large amounts of data (e.g. a file), you'll likely want to do so in chunks:
const hashContext = s4.hash_init(S4HashAlgorithm.SHA3_256);
while (...got another chunk of data of type Uint8Array...)
{
s4.hash_update(hashContext, data);
}
const hashData = s4.hash_final(hashContext);
s4.hash_free();
Todo...