Pratique OpenSSL basée sur le wasm

L'article précédent a partagé le concept et l'utilisation de base de WebAssembly, et a acquis une compréhension générale de WebAssembly grâce à l'analyse de deux exemples de code. Cet article partage la pratique des outils de chiffrement basés sur WebAssembly. Prenons les algorithmes digest md5 et sha1 de openssl comme exemples pour compiler openSSL en WebAssembly sur Mac.

alentours

  • Emscripten version 2.0.3
  • OpenSL version 1.1.1d
  • Version du navigateur 85.0.4183.121 (version officielle) (64 bits)

Aperçu

  • Compilez openSSL en WebAssembly sur Mac
  • Les problèmes rencontrés
  • Pour résumer

1. Compilez openSSL en WebAssembly sur Mac

L'ensemble du processus de compilation d'Openssl en WebAssembly est comme ceci, fichier md5.c -> compilation emscripten -> fichier. Wasm -> combiné avec l'API WebAssembly JS -> s'exécutant dans le navigateur.

1. fichier md5.c
//md5.c
#include <emscripten.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#include <string.h>
#include <stdio.h>

EMSCRIPTEN_KEEPALIVE
void md5(char *str, char *result,int strlen) {
    MD5_CTX md5_ctx;
    int MD5_BYTES = 16;
    unsigned char md5sum[MD5_BYTES];
    MD5_Init(&md5_ctx);  
    MD5_Update(&md5_ctx, str,strlen);
    MD5_Final(md5sum, &md5_ctx);
    char temp[3] = {0};
    memset(result,0, sizeof(char) * 32);
    for (int i = 0; i < MD5_BYTES; i++) {
        sprintf(temp, "%02x", md5sum[i]);
        strcat(result, temp);
    }
    result[32] = '\0';
}

EMSCRIPTEN_KEEPALIVE
void sha1(char *str, char result[],int strlen) {
    unsigned char digest[SHA_DIGEST_LENGTH];
    SHA_CTX ctx;
    SHA1_Init(&ctx);
    SHA1_Update(&ctx, str, strlen);
    SHA1_Final(digest, &ctx);
    for (int i = 0; i < SHA_DIGEST_LENGTH; i++){
        sprintf(&result[i*2], "%02x", (unsigned int)digest[i]);
    }
}

Le fichier md5.c contient deux fonctions md5 et sha1, qui seront utilisées pour compiler vers wasm plus tard.

Tips: 
1. 默认情况下,Emscripten 生成的代码只会调用 main() 函数,其它的函数将被视为无用代码。在一个函数名之前添加 EMSCRIPTEN_KEEPALIVE 能够防止这样的事情发生。你需要导入 emscripten.h 库来使用 EMSCRIPTEN_KEEPALIVE。
2. 内部实现调用的是openssl提供的函数,简单封装下直接调用即可。
2. Compilation Emscripten
Téléchargez openssl et générez Makefile

La version d'OpenSSL que j'utilise est 1.1.1d, adresse: https://github.com/openssl/openssl/releases/tag/OpenSSL_1_1_1d
Après la décompression, entrez dans le dossier openssl-OpenSSL_1_1_1d. Compilez et générez Makefile.

emcmake ./Configure  darwin64-x86_64-cc -no-asm --api=1.1.0

Modifiez le Makefile généré. Si vous ne le modifiez pas, des erreurs de compilation risquent de se produire.

  • Remplacez CROSS_COMPILE = / usr / local / Cellar / emscripten / 1.38.44 / libexec / em par CROSS_COMPILE =
  • Remplacez CNF_CFLAGS = -arch x86_64 par CNF_CFLAGS =
Compiler openssl
emmake make -j 12 build_generated libssl.a libcrypto.a
mkdir -p ~/resource/openssl/libs
cp -R include ~/resource/openssl/include
cp libcrypto.a libssl.a ~/Downloads/openssl/libs

Création d'un répertoire openssl, en fait, pour référencer l'emplacement de la bibliothèque statique dans md5.c. Une fois la compilation réussie, deux fichiers, libssl.a et libcrypto.a, apparaîtront dans le dossier.

Compiler wasm
emcc md5.c -I ~/resource/openssl/include -L ~/resource/openssl/libs -lcrypto -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap", "ccall"]' -o md5.js

Après une compilation réussie, deux fichiers md5.js et md5.wasm seront générés.

Tips: 
Emscripten从v1.38开始,ccall/cwrap辅助函数默认没有导出,在编译时需要通过-s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap']"选项显式导出。
3. Appelez le fichier wasm

Utilisez l'API JS WebAssembly pour appeler wasm. Les codes md5 et sha1 sont placés dans md5.html et sont utilisés de la même manière. Seuls les codes associés à md5 sont affichés dans le texte. Adresse de code: https://github.com/likai1130/study/blob/master/wasm/openssl/demo/md5.html

<div>
    <div>
        <input type="file" id="md5files" style="display: none" onchange="md5fileImport();">计算md5
        <input type="button" id="md5fileImport" value="导入">
    </div>
</div>

<script src="jquery-3.5.1.min.js"></script>
<script src="md5.js"></script>
<script type='text/javascript'>
    Module = {};
    const mallocByteBuffer = len => {
        const ptr = Module._malloc(len)
        const heapBytes = new Uint8Array(Module.HEAPU8.buffer, ptr, len)
        return heapBytes
    }
    //点击导入按钮,使files触发点击事件,然后完成读取文件的操作
    $("#md5fileImport").click(function() {
        $("#md5files").click();
    })
    function md5fileImport() {
        //获取读取我文件的File对象
        var selectedFile = document.getElementById('md5files').files[0];
        var name = selectedFile.name; //读取选中文件的文件名
        var size = selectedFile.size; //读取选中文件的大小
        console.log("文件名:" + name + "大小:" + size);
        var reader = new FileReader(); //读取操作就是由它完成.
        reader.readAsArrayBuffer(selectedFile)
        reader.onload = function() {
            //当读取完成后回调这个函数,然后此时文件的内容存储到了result中,直接操作即可
            console.log(reader.result);
            const md5 = Module.cwrap('md5', null, ['number', 'number'])                 const inBuffer = mallocByteBuffer(reader.result.byteLength)
            var ctx = new Uint8Array(reader.result)                 inBuffer.set(ctx)
            const outBuffer = mallocByteBuffer(32)
            md5(inBuffer.byteOffset,outBuffer.byteOffset,inBuffer.byteLength)
            console.log("md5值= ",Array.from(outBuffer).map(v => String.fromCharCode(v)).join(''))
            Module._free(inBuffer);
            Module._free(outBuffer);
        }
    }
</script>
4. Exécutez dans le navigateur

Le fichier a.out est une donnée binaire
md5: 0d3c57ec65e81c7ff6da72472c68d95b
sha1: 9ef00799a4472c71f2177fd7254faaaadedb0807

Insérez la description de l'image ici
Insérez la description de l'image ici
L'un est md5 et sha1 calculé par le programme, et l'autre est md5 et sha1 calculé par openssl sur le système, indiquant que la pratique de compiler openssl par Webassembly est réussie.

2. Problèmes rencontrés

La chaîne d'appels est la suivante:

md5.js (胶水代码)<-----> md5.c <-----> openssl API
Problème de communication de données

Dans tout le processus de pratique, le problème le plus gênant est le problème de communication des données. Il est très difficile de transférer des structures de données complexes entre C / C ++ et JS, et il faut faire fonctionner la mémoire pour y parvenir.

  • Échange de données Javascript et C / C ++

    typescript
    #md5.wasm解析后的md5函数在wasm文件中的代码
    func $md5 (;3;) (export "md5") (param $var0 i32) (param $var1 i32) (param $var2 i32)

    Parce que wasm ne peut actuellement importer et exporter que des API de style de fonction en langage C, et les paramètres n'ont que quatre types de données (i32, i64, f32, f64), qui sont tous des nombres, qui peuvent être compris comme des codes binaires nus, et il est impossible de passer directement des codes complexes. Type et structure des données. Par conséquent, dans le navigateur, ces API de haut niveau doivent être encapsulées par JS et un mécanisme est nécessaire pour implémenter la conversion multilingue de structures de données complexes.

    • Module.buffer

      Que la cible de compilation soit asm.js ou wasm, l'espace mémoire aux yeux du code C / C ++ correspond en fait à l'objet ArrayBuffer fourni par Emscripten: Module.buffer, et l'adresse mémoire C / C correspond à l'indice du tableau module.buffer un par un.

      function md5fileImport() {
         var selectedFile =   document.getElementById('md5files').files[0];
         var name = selectedFile.name; //读取选中文件的文件名
         var size = selectedFile.size; //读取选中文件的大小
         console.log("文件名:" + name + "大小:" + size);
         var reader = new FileReader(); //这是核心,读取操作就是由它完成.
      
         reader.readAsArrayBuffer(selectedFile)
         .....
      }

      Dans le code, nous utilisons reader.readAsArrayBuffer () pour lire le fichier et renvoyer un tableau ArrayBuffer. Mais vous ne pouvez toujours pas appeler la fonction C, vous devez créer un tableau typé, tel que Int8Array, UInt32Array, et utiliser son format spécifique comme vue de ce morceau de données binaires pour effectuer des opérations de lecture et d'écriture.

      Tips:
          C/C++代码能直接通过地址访问的数据全部在内存中(包括运行时堆、运行时栈),而内存对应Module.buffer对象,C/C代码能直接访问的数据事实上被限制在Module.buffer内部。

      La mémoire de WebAssembly est également un ArrayBuffer Le module encapsulé par Emscripten fournit différentes vues telles que Module.HEAP8 et Module.HEAPU8. Attaché:
      Insérez la description de l'image ici

  • Accéder à la mémoire C / C ++ en JavaScript

Le calcul de md5 / sha1 nécessite javascript pour saisir une grande quantité de données dans l'environnement C / C ++, et C / C ++ ne peut pas prédire la taille du bloc de données. À ce stade, vous pouvez allouer de la mémoire dans JavaScript et charger les données, puis transmettre le pointeur de données et appeler la fonction C Procéder.

Tips:
这种用法之所以可行,核心原因在于:Emscripten导出了C的malloc()/free()

J'ai déclaré la méthode d'allocation d'espace mémoire comme méthode publique.

        Module = {};
        const mallocByteBuffer = len => {
            const ptr = Module._malloc(len)
            const heapBytes = new Uint8Array(Module.HEAPU8.buffer, ptr, len)
            return heapBytes
        }

        function md5fileImport() {
            //获取读取我文件的File对象
            var selectedFile = document.getElementById('md5files').files[0];
            ......
            var reader = new FileReader(); //这是核心,读取操作就是由它完成.
            reader.readAsArrayBuffer(selectedFile)
            reader.onload = function() {
                //当读取完成后回调这个函数,然后此时文件的内容存储到了result中,直接操作即可
                const md5 = Module.cwrap('md5', null, ['number', 'number'])
                const inBuffer = mallocByteBuffer(reader.result.byteLength)
                var ctx = new Uint8Array(reader.result)
                inBuffer.set(ctx)
                const outBuffer = mallocByteBuffer(32)
                md5(inBuffer.byteOffset,outBuffer.byteOffset,inBuffer.byteLength)

                console.log("md5值= ",Array.from(outBuffer).map(v => String.fromCharCode(v)).join(''))
                Module._free(inBuffer);
                Module._free(outBuffer);
            }
        }
Tips: 
C/C++的内存没有gc机制,在JavaScript中使用malloc()函数分配的内存使用结束后,需要使用free()将其释放。

En outre, Emscripten fournit également une série de fonctions auxiliaires telles que AsciiToString () / stringToAscii () / UTF8ArrayToString () / stringToUTF8Array () pour gérer la conversion de chaînes dans divers formats dans divers objets de stockage. Pour plus de détails, veuillez vous référer à vous-même Code de colle.

Trois, résumé

La relation d'appel complète de openssl basée sur wasm:

Insérez la description de l'image ici

Le problème technique rencontré dans cette pratique est le problème de la communication de données. Un autre problème est l'idée. J'ai toujours pensé que tout openssl compilé dans un fichier .wasm pouvait être utilisé. Il s'avère que vous devez utiliser du code glue pour pouvoir l'utiliser. Utilisé sur le Web. Ensuite, il y a une question. Le fichier Wasm est essentiellement un fichier binaire. Existe-t-il un outil qui peut être exécuté directement. Fichier Wasm, WAPM (WebAssembly Package Manager) Il s'agit d'un outil de gestion de package pour WebAssembly. Apprenons à connaître l'outil de gestion de package WebAssembly dans l'article suivant. .

Référence


Netwarps est composé d'une équipe senior de cloud computing et de développement de technologies distribuées en Chine et possède une expérience très riche dans les secteurs de la finance, de l'énergie, des communications et d'Internet. Netwarps dispose actuellement de centres de R&D à Shenzhen et à Pékin, avec une équipe de plus de 30 personnes, dont la plupart sont des techniciens avec plus de dix ans d'expérience en développement, issus de domaines professionnels tels que l'Internet, la finance, le cloud computing, la blockchain et les institutions de recherche scientifique.
Netwarps se concentre sur la R&D et l'application de produits technologiques de stockage sécurisé. Les principaux produits sont le système de fichiers décentralisé (DFS) et la plate-forme informatique décentralisée (DCP), engagés à fournir un stockage distribué et distribué sur la base d'une technologie de réseau décentralisé. La plate-forme informatique présente les caractéristiques techniques d'une haute disponibilité, d'une faible consommation d'énergie et d'un faible réseau, et convient à des scénarios tels que l'Internet des objets et l'Internet industriel.
Compte officiel: Netwarps

Je suppose que tu aimes

Origine blog.51cto.com/14915984/2561738
conseillé
Classement