📜 ⬆️ ⬇️

Tcl / Tk - creating extensions / packages based on dynamic libraries

In one of our notes , the intention was to write graphical skins for the OpenSSL and NSS (Network Security Services) packages. GUI for NSS was written :

image

It should be noted that today openssl with GOST engine is gaining increasing popularity in the vast Russian PKI (public key infrastructure).

And that is why the development of an OpenSSL extension for Tcl can be quite popular. And now we will show how you can develop a GUI for OpenSSL, but not based on the openssl command utility, but using the openssl libraries directly (libcrypto, libssl).
')
image Directly native libraries OpenSSL can not be used. As in many other cases (a vivid example of Java), you need to write a wrapper (wrap), taking into account the features of Tcl. But these features are transparent and do not cause difficulties.

First you need to decide on the syntax of the created extension, and in fact on the syntax of accessing openssl functions. And so, any Tcl program consists of commands, separated by newline characters or semicolons. I recommend the semicolon to always put at the end of the command. In turn, each team consists of a set of fields, separated by spaces. The first field is the name of the command, and the optional remaining fields are the arguments passed to this command. The command returns the result. Based on this and based on the syntax of the openssl utility, you can suggest the following syntax for the openssl extension:

lscw <> [ 1] [ 2] … [ N]; 

where lscw is the name of the command. The <operation> field, by analogy with the openssl command line utility (for example, openssl x509), will indicate the subfunction being executed. Subfunction parameters are set after it.

We will consider the creation of an extension or, as it is also called, a package by the example of obtaining the contents of a certificate (or certificate request) and then displaying it in a widget. The tcl command might look like this:

 set dump [lscw print $file_cert x ]; 

which is interpreted as follows: save in the dump variable the contents (print parameter) of the certificate (x parameter) located in the file, the path to which is stored in the $ file variable. All parameters are positional. If r / R is set instead of the x / X parameter, the contents of the certificate request (PKCS # 10) will be printed, if c / C is the list of revoked certificates.

Support for commands from the package is supported by the corresponding library. To load this library, use the load Tcl command, which in the simplest case is the following:

 load <> < >; 

The first parameter of the command is the library name, and the second is the name of the package that it implements. Now, knowing all this and deciding that the package will have the name lscw, we can write a Tcl / Tk script that allows you to load the extension library, select the file with the certificate and view it in the window (there is a similar script for NSS ):

 encoding system utf-8; #    . configure -background {#FFDAB9}; #    wm title . {GUI OpenSSL X509}; set homeDir $env(HOME); set types {{{ liblscw} {.so} } {{ liblscw MS} {.dll} } {{ } * } }; set typesCert { {{} {.crt} } {{} {.cer} } {{} {.der} } {{} {.pem} } {{ } * } }; #   wm iconify .; set liblscw [tk_getOpenFile -filetypes $types -initialdir $homeDir -title {    OpenSSL}]; if { $liblscw == {} } {exit 1}; # wrap-  OpenSSL if {[catch {load $liblscw lscw} res]} { puts $res; exit 1; }; set loadlib {loading shared lib: }; set loadlib $loadlib$liblscw; puts $loadlib; #puts {loading shared lib: $liblscw} set fileCert [tk_getOpenFile -filetypes $typesCert -initialdir $homeDir -title { /  }]; if { $fileCert == {} } {exit 1}; if {[catch {lscw print $fileCert x} res]} { puts $res; exit 1; }; #   wm deiconify .; # text-    text .prcert -background snow; pack .prcert -expand 1 -fill both -in . -pady 6 -padx 6; #  button .but -text  -command exit -background orange -activebackground green; pack .but -pady 6; #    .prcert delete 0.0 end ; #  .prcert insert end $res; # 

The code itself does not require additional comments. Now we have to create a library in the C language. Speaking about the extension for OpenSSL, we are interested in the support of Russian cryptography in it. In standard OpenSSL, this support is provided through engine gost. That is why in our library should be provided to connect this engine.

As we already said, the library is loaded using the load command, in which the first parameter is the path to the library, and the second is the package name.
The package name actually specifies the name of the initialization procedure, which is called immediately after the library is loaded. The name of the initialization procedure is as follows:

 < >_Init 

We note another feature naming the initialization procedure: the first character of the package name is always converted to uppercase. That is why the team

 load $liblscw lscw; 

and team

 load $liblscw Lscw; 

are identical and when loading the library in both cases, the Lscw_Init initialization procedure will be called:

 int Lscw_Init(Tcl_Interp* interp) { OpenSSL_add_all_algorithms(); #ifdef OPENSSL_GOST ENGINE_load_builtin_engines(); // - engine // gost = load_engine("/usr/lib64/openssl/1.0.2m/engines/libgost.so", 0); gost = load_engine("OPENSSL/libgost.so", 0); if (!ENGINE_set_default (gost, ENGINE_METHOD_ALL)) { Tcl_SetResult(interp, "OpenSSL error: ENGINE_set_default failed on engine", (Tcl_FreeProc*)0); return TCL_ERROR; } #endif //  lscw   lsrapCmd Tcl_CreateCommand(interp, "lscw", (Tcl_CmdProc *)lswrapCmd, (ClientData) 0, (void (*)()) NULL); return TCL_OK; } 

Full library code can be found here.
 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/pem.h> #include <openssl/engine.h> #include <tcl.h> #include <tk.h> #ifdef OPENSSL_GOST void ENGINE_load_gost(void); static ENGINE *gost; static ENGINE *load_engine(const char *engine, int debug); #endif char *X509View(char *nickfile, char typeX509); int lscwPrintSubCmd(ClientData lscmd, Tcl_Interp* interp); /*   */ typedef int ((*subProc)(ClientData, Tcl_Interp*)); /* */ typedef struct { char* subname; //  subProc f; //  int minStack; //  } subEntry; static subEntry subTable[] = { {"print", lscwPrintSubCmd, 2}, //   { 0, 0} }; int lscwPrintSubCmd(ClientData lscmd, Tcl_Interp* interp) { char **argv; char *res; argv = (void *)lscmd; res = (char *)X509View((char*)argv[2], (char)argv[3][0]); if (res == NULL) { //    Tcl Tcl_SetResult(interp, " .", (Tcl_FreeProc*)0); //  Tcl   TCL_ERROR return TCL_ERROR; } //    Tcl Tcl_SetResult(interp, res, (Tcl_FreeProc*)0); //  Tcl   TCL_ERROR return TCL_OK; } char *X509View(char *nickfile, char typeX509) { X509 *x509 = NULL; X509_REQ *x509_req = NULL; BIO *mem = BIO_new(BIO_s_mem()); char *p = NULL; char *res; int len; //  BIO *bio; if ( NULL == ( bio = BIO_new_file(nickfile, "rb"))){ fprintf(stderr, "X509View: failed to open file=%s\n", nickfile); return NULL; } switch(typeX509){ /*  X509*/ case 'x': case 'X': // DER x509 = d2i_X509_bio(bio, NULL); if (NULL == x509){ // PEM BIO_reset( bio ); x509 = PEM_read_bio_X509( bio, NULL, NULL, NULL ); if (NULL == x509){ BIO_free(bio); return NULL; } } #ifdef OPENSSL_GOST X509_print_ex(mem, x509, XN_FLAG_SEP_MULTILINE|ASN1_STRFLGS_UTF8_CONVERT, X509_FLAG_COMPAT); #else X509_print_ex(mem, x509, XN_FLAG_SEP_MULTILINE|ASN1_STRFLGS_UTF8_CONVERT, X509_FLAG_COMPAT); // X509_print(mem , x509); #endif X509_free(x509); break; /*    PKCS#10*/ case 'r': case 'R': // DER x509_req = d2i_X509_REQ_bio(bio, NULL); if (NULL == x509_req){ // PEM BIO_reset( bio ); x509_req = PEM_read_bio_X509_REQ( bio, NULL, NULL, NULL ); if (NULL == x509_req){ BIO_free(bio); return NULL; } } X509_REQ_print(mem, x509_req); X509_REQ_free(x509_req); break; /*    CRL*/ case 'c': case 'C': /* */ fprintf(stderr, " !!! file=%s, type=%c\n", nickfile, typeX509); break; defaut: return NULL; } len = BIO_get_mem_data(mem, &p); if(len == 0){ BIO_free(mem); fprintf(stderr, "BIO_get_mem_data ERROR!!! file=%s, type=%c\n", nickfile, typeX509); return NULL; } res = strdup(p); res[len - 1] = '\0'; BIO_free(mem); return res; } /*   */ static int lswrapCmd(ClientData dummy, Tcl_Interp* interp, int argc, char** argv) { int i; int retv; Tcl_ResetResult(interp); if (argc < 2) return TCL_ERROR; //  for(i = 0; subTable[i].subname != 0; i++) if(strcmp(argv[1], subTable[i].subname) == 0) { if((argc -2) != subTable[i].minStack){ char er[1024]; Tcl_SetResult(interp, "Usage: lircw Init gui|line", (Tcl_FreeProc*)0); sprintf(er, " not enough args: dano=%i, nado=%i ", (argc -2), subTable[i].minStack); Tcl_AppendResult(interp, er, (char*)0); return TCL_ERROR; } //  retv = subTable[i].f((void*)argv, interp); return retv; } if(subTable[i].subname == 0) { Tcl_AppendResult(interp, "   lscw: ", argv[1], 0); return TCL_ERROR; } } int Lscw_Init(Tcl_Interp* interp) { OpenSSL_add_all_algorithms(); #ifdef OPENSSL_GOST ENGINE_load_builtin_engines(); // - engine // gost = load_engine("/usr/lib64/openssl/1.0.2m/engines/libgost.so", 0); gost = load_engine("OPENSSL/libgost.so", 0); if (!ENGINE_set_default (gost, ENGINE_METHOD_ALL)) { fprintf(stderr, "OpenSSL error: ENGINE_set_default failed on engine \n"); return TCL_ERROR; } #endif Tcl_CreateCommand(interp, "lscw", (Tcl_CmdProc *)lswrapCmd, (ClientData) 0, (void (*)()) NULL); return TCL_OK; } #ifdef OPENSSL_GOST //  engine static ENGINE *load_engine(const char *engine, int debug) { ENGINE *e = ENGINE_by_id("dynamic"); if (e) { if (!ENGINE_ctrl_cmd_string(e, "SO_PATH", engine, 0) || !ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) { ENGINE_free(e); e = NULL; } } return e; } #endif 


We used static openssl libraries for building:

 $cc -shared -o liblscw.so -DOPENSSL_GOST -DUSE_INTERP_RESULT -fPIC openssl_print.c OPENSSL/libcrypto.a -ldl -pthread –lz $ 

After assembly, you can safely run the script at the beginning of the article.

Now it's time to dwell on OpenSSL with support for Russian cryptoalgorithms Unfortunately, the GOST-engine, which is part of OpenSSL, does not support new cryptographic algorithms , in particular:

image elaboration of a hash function in accordance with the requirements of GOST R 34.11-2012 “Information technology. Cryptographic protection of information. Hash function ";

• formation and verification of EDS in accordance with the requirements of GOST R 34.10-2012 “Information technology. Cryptographic protection of information. Processes of formation and verification of electronic digital signature ";

• Encryption / decryption of data with the Grasshopper encryption algorithms (Kuzmin, Nechaev and Company) and Magma in accordance with the requirements of “GOST R 34.12-2015 Information technology. Cryptographic protection of information. Block ciphers "and" GOST R 34.13-2015 Information technology. Cryptographic protection of information. Modes of block ciphers. "

There are still flaws. Today, you can find modifications of OpenSSL, including those certified in the certification system of the FSB of Russia, with full support of Russian cryptography and meeting all regulatory requirements, including qualified certificates. The lscw library compiled on one of these projects can be downloaded here and used to view qualified certificates:

image

And finally, I wanted to get away from the tcl-script and get a real binary code. To do this, remove the tcl script in the C-code openssl_print_main.c:

Openssl_print_main.c source code
 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <tcl.h> #include <tk.h> //  Tcl/Tk -  char strtcl[] = " \ encoding system utf-8; \ . configure -background {#FFDAB9}; \ wm title . {GUI OpenSSL X509}; \ set homeDir $env(HOME); \ set types {{{ liblscw} {.so} } \ {{ liblscw MS} {.dll} } \ {{ } * } \ }; \ set typesCert { {{} {.crt} } \ {{} {.cer} } \ {{} {.der} } \ {{} {.pem} } \ {{ } * } \ }; \ wm iconify . ; \ set liblscw [tk_getOpenFile -filetypes $types -initialdir $homeDir -title {    OpenSSL}]; \ if { $liblscw == {} } {exit 1}; \ if {[catch {load $liblscw} res]} { \ puts $res; \ exit 1; \ }; \ set loadlib {loading shared lib: } ; \ set loadlib $loadlib$liblscw ; \ puts $loadlib ; \ set fileCert [tk_getOpenFile -filetypes $typesCert -initialdir $homeDir -title { /  }]; \ if { $fileCert == {} } {exit 1}; \ if {[catch {lscw print $fileCert x} res]} { \ puts $res; \ exit 1; \ }; \ wm deiconify .; \ text .prcert -background snow; \ pack .prcert -expand 1 -fill both -in . -pady 6 -padx 6; \ button .but -text  -command exit -background orange -activebackground green; \ pack .but -pady 6; \ .prcert delete 0.0 end ; \ .prcert insert end $res; \ "; Tcl_Interp * tcl_interp ; main(int argc, char* argv[]){ int code; Tcl_FindExecutable(argv[0]); /* tcl-*/ tcl_interp = Tcl_CreateInterp(); /*  tcl-*/ Tcl_Init(tcl_interp); /* Tk-    tcl-*/ Tk_Init(tcl_interp); /*  Tcl/Tk   */ code = Tcl_Eval(tcl_interp, strtcl); /* Tcl/Tk      : code = Tcl_EvalFile(tcl_interp, filetcl); */ /*      lenstr   */ // code = Tcl_Eval(tcl_interp, ".but3 configure -command lenstr;"); /*   "lenstr"   lenStr   C-*/ // Tcl_CreateCommand(tcl_interp, "lenstr", (Tcl_CmdProc *)lenStr, NULL, NULL); Tk_MainLoop(); fprintf(stderr, "!\n"); return 0; } 
broadcast it
 $cc -o openssl_print_main openssl_print_main.c -ltcl –ltk $ 

And we do:

 $./openssl_print_main 

image

Choose a library, then a certificate and admire (see above).

The final project for using OpenSSL in Tcl / Tk is:

image

The project can be downloaded here .

Now it is up to small, for Android:

image

Source: https://habr.com/ru/post/345574/


All Articles