- 
                Notifications
    
You must be signed in to change notification settings  - Fork 14
 
Description
GMSSL is a popular library for china national shangmi(SM) algorithms,  include sm2, sm3, sm4 and related.  But do not have a good implements of Javascript language. also there was another robust library for Shangmi, but it was not used in finance yet. after few days working,  today i gonna show you how to use EMScripten to compile it and use it in javascript within browser.
requirements
- ubuntu 22.04 LTS
 - gmssl
 
Install EMScripten and Compile GMSSL
sudo apt install emscripten llvm clang
git clone https://github.com/guanzhi/GmSSL
cd GmSSL && mkdir build
cd build
emcmake cmake ..
emmake make -j8when you finished the compile process. you will get a lot of wasm file and js file. but it can not work correctly yet.

and you  have a static library was named libgmssl.a now.
write c code and compile to WASM with gmssl lib(libgmssl.a)
you should write your own code and compile it with libgmssl.a
#ifdef __EMSCRIPTEN__  
#include <emscripten.h>  
EMSCRIPTEN_KEEPALIVE  
char* run_test_sm3(const char *test_data) {  
    return test_sm3(test_data);  
}  
EMSCRIPTEN_KEEPALIVE  
SM2Keys* run_test_sm2_keys(void) {  
    return generate_sm2_keys();  
} 
EMSCRIPTEN_KEEPALIVE  
char* run_test_sm2_encrypt(const char* public_key_hex, const char* plaintext) {  
    return test_sm2_encrypt(public_key_hex, plaintext);  
}  
EMSCRIPTEN_KEEPALIVE  
char* run_test_sm2_decrypt(const char* private_key_hex, const char* ciphertext) {  
    return test_sm2_decrypt(private_key_hex, ciphertext);  
} 
EMSCRIPTEN_KEEPALIVE
char* run_test_sm2_sign(const char* private_key_hex, const char* message){
	return test_sm2_sign(private_key_hex, message);
}
EMSCRIPTEN_KEEPALIVE
int run_test_sm2_verify(const char* public_key_hex, const char* message, const char* signature_hex){
	return test_sm2_verify(public_key_hex, message, signature_hex);
}
#endif  those code was demonstrated how to keep your c code can be calling in the Javascript.  Once you compiled those c code, you will get a wasm file and js file.
use below shell script to compile it
echo "【Clearing】Remove old complied files....."
rm sm.html sm.js sm.wasm
echo "【Start】Start to building....."
static_gmssllib_path=$PWD/libgmssl.a
source_code=smtest.c
emcc -I/usr/local/include $static_gmssllib_path $source_code \
-s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap","UTF8ToString", "stringToUTF8", "getValue"]' \
-o sm.html
echo "【Done】Build complete"
if you need to return value from c function, you must export runtime function : UTF8ToString
call it from javascript
<html>
<head>
    <script src="sm.js"></script>
    <script>
        function stringToHex(str) {
            let hex = '';
            for (let i = 0; i < str.length; i++) {
                hex += str.charCodeAt(i).toString(16).padStart(2, '0');
            }
            return hex;
        }
        function hexToString(hex) {
            // Ensure the hex string length is even  
            if (hex.length % 2 !== 0) {
                throw new Error("Invalid hex string");
            }
            let str = '';
            for (let i = 0; i < hex.length; i += 2) {
                // Parse each pair of hex digits as an integer, then convert to a character  
                const byte = parseInt(hex.substr(i, 2), 16);
                str += String.fromCharCode(byte);
            }
            return str;
        }
        setTimeout(() => {
            const run_test_sm3 = Module.cwrap('run_test_sm3', 'string', ['string']);
            // Call the wrapped function  
            // const hexString = '74657374'; // 'test' in hex  
            const result = run_test_sm3(stringToHex('test'));
            // Output the result  
            console.log("SM3 Hash:", result);
            const run_generate_sm2_keys = Module.cwrap('run_test_sm2_keys', 'number', []);
            const keysPtr = run_generate_sm2_keys();
            const privateKeyPtr = Module.getValue(keysPtr, 'i32');
            const publicKeyPtr = Module.getValue(keysPtr + 4, 'i32');
            const privateKey = Module.UTF8ToString(privateKeyPtr);
            const publicKey = Module.UTF8ToString(publicKeyPtr);
            console.log("Private Key:", privateKey);
            console.log("Public Key:", publicKey);
            const run_sm2_encrypt = Module.cwrap('run_test_sm2_encrypt', 'string', ['string', 'string']);
            const run_sm2_decrypt = Module.cwrap('run_test_sm2_decrypt', 'string', ['string', 'string']);
            const run_sm2_sign = Module.cwrap('run_test_sm2_sign', 'string', ['string', 'string']);
            const run_sm2_verify = Module.cwrap('run_test_sm2_verify', 'int', ['string', 'string', 'string']);
            var plaintext = "This is funny";
            try {
                var ciphertextHex = run_sm2_encrypt(publicKey, plaintext);
                console.log("Ciphertext (hex):", ciphertextHex);
                var dec_cipher = run_sm2_decrypt(privateKey, ciphertextHex);
                console.log("Plaintext:", dec_cipher);
                // pass param to function to c with stringToHex
                var signature = run_sm2_sign(privateKey, stringToHex(plaintext));
                console.log("Plaintext signature:", signature);
                var verify_result = run_sm2_verify(publicKey, stringToHex(plaintext), signature);
                if (verify_result == 1) {
                    console.log("Signature Verify: passed",);
                } else {
                    console.log("Signature Verify: failed",);
                }
            } catch (e) {
                console.error("An error occurred:", e);
            }
        }, 1000);
    </script>
</head>
</html>and run python -m http.server to host the html, then you will see:
all exported function can be called by wasm with Module.cwrap('run_test_sm2_keys', 'number', []); and
Troubleshooting
- RuntimeError: Aborted(Assertion failed: native function 
run_test_sm3called before runtime initialization) 
  setTimeout(()=>{
    Module._run_test_sm3('test');  
  }, 1000);- xxx function was not exported
change your function in the below function list 
-s EXPORTED_FUNCTIONS='["_run_test_sm3", "_hex_to_bytes", "_sm3_init", "_sm3_update", "_sm3_finish"]'- if you need return a value in c but can not accept it in js
 
just export runtime function: UTF8ToString
EXPORTED_RUNTIME_METHODS='["ccall", "cwrap","UTF8ToString", "stringToUTF8", "getValue"]'- 
if memroy error with
Module._xxx
Just replace it withModule.cwrap('run_test_sm2_keys', 'number', []); - 
compare two hash was equal in sign function and verify function
gmssl was accept hex value by default. 

