How to configure Linux to enter the domain using GOST algorithms

Introduction









The Kerberos 5 protocol is now actively used for authentication. A feature of this protocol is that it performs authentication based on four pillars:







  1. Symmetric encryption;
  2. Hashing
  3. EDS;
  4. Third Trusted Party.


Starting with the fifth version, it became possible to use more asymmetric encryption (for electronic signature). It does not make sense to dwell on the operation of the Kerberos protocol, because the description of the algorithm can be found here .







Unfortunately, the number of encryption, hashing and digital signature algorithms that this protocol uses is not as large as I would like, so in this article I want to show how to add simply and easily own algorithms for the implementation of this protocol by MIT . We will add our domestic algorithms: GOST 28147-89 (aka Magma), GOST R 34.11-2012 (aka Stribog) and GOST R 34.10-2012 (I would also like to have aka for it, but I do not know :(). Ready a solution for these algorithms can be found in my repository . On the client side, we will use hardware implementations of GOST algorithms in Rutoken EDS 2.0 and their software implementations in engine GOST for openssl. But the safest option for storing keys is when they are generated directly on Rutoken and never do not leave his memory during cryptographic operations For this option, rtengine is required.







Before starting to implement the algorithms, we describe the main places where changes will be made. Inside the src / lib / crypto / directory are the implementations of all the algorithms responsible for symmetric cryptography and hashing. It has 2 implementations of these cryptographic algorithms: builtin and openssl. In order to save time and space, we, of course, will add implementation of algorithms using openssl, in which they already exist (well, or almost there, but more on that later). To add asymmetric algorithms, you will need to tweak the src / plugins / preauth / pkinit plugin







If you have not yet configured Kerberos, then instructions for its initial configuration and operation can be found here . In the future, the author believes that you are working with the AKTIV-TEST.RU domain







Adding Hash Algorithms and Symmetric Encryption



As it was previously announced, we will not write encryption and hashing algorithms from scratch, but will use a ready-made implementation of these algorithms in openssl. Openssl directly does not support the implementation of domestic algorithms, but it has mobility in this matter and allows you to add new algorithms using the engine GOST mechanism for working with keys in the file system and stored on the token - rtengine.







A small introduction to the openssl engine mechanism and connecting the engine GOST



Engine in openssl is a small dynamic library that openssl loads into runtime on demand. Each such library must contain certain symbols (functions) to load the necessary algorithms. In this paper, we will use engine gost, which contains all the necessary domestic cryptographic algorithms.







Installation is wilderly simple, for example, and proceeds as follows:







  1. Download the implementation of this engine from the repository .







  2. build a library with it (mkdir build && cd build && cmake ... && make):







    mkdir build cd build cmake .. make
          
          





  3. In the bin directory (WHICH WILL APPEAR IN THE ROOT CATALOG OF THE PROJECT !!!) there will be a dynamic library gost.so - this is our Engin. It will need to be moved to the directory where the openssl engines are stored. You can find out the location of this directory using:







     openssl version -a | grep ENGINESDIR
          
          





  4. It’s up to the latter - you need to tell openssl where the given engine is and what it's called. You can do this by changing the openssl.cnf configuration file. The location of which can be found using:







     openssl version -a | grep OPENSSLDIR
          
          





    At the end of this file you will need to add the following contents:







     #     openssl_conf = openssl_def ... #    # OpenSSL default section [openssl_def] engines = engine_section # Engine section [engine_section] gost = gost_section # Engine gost section [gost_section] engine_id = gost dynamic_path = /path/to/engines/dir/with/gost.so default_algorithms = ALL init = 0
          
          







After that, this engine should appear in openssl. You can check its performance by forcing, for example, to generate a private key in a file according to GOST R 34.10-2012:







 openssl genpkey -engine gost -algorithm gost2012_512 -pkeyopt paramset:A -out client_key.pem
      
      





The -engine flag just indicates which engine needs to be loaded before starting work so that the key generation algorithm for GOST R 34.10-2012 becomes visible.







Hash algorithm implementation



Let's start with the simplest - with the implementation of the Stribog algorithm. Kerberos has a strong connection between the hashing and encryption algorithms, that is, you cannot just pick and choose one algorithm for encryption, and for hashing another, you will need to integrate a hash and encryption algorithm. The reason for this is not known to me, but since there are such rules there - let's try to create a combination of the Stribog algorithm and AES.







  1. Because we need confidence in the connection in different places of our program, let's first create a small gost_helper library that will contain the initialization function of the engine in openssl, as well as for convenience, several functions that return the contexts of some hashing algorithms - this will help us in the future. We name this library gost_helper and create an appropriate header and source file for it in the src / lib / crypto / openssl / directory:







     // gost_helper.h #include <openssl/evp.h> //    GOST void krb5int_init_gost(); //  ,     const EVP_MD * EVP_gostR3411_2012_256(); const EVP_MD * EVP_gostR3411_2012_512(); // gost_helper.c #include "gost_helper.h" #include <openssl/engine.h> //     static ENGINE *eng = NULL; void krb5int_init_gost() { //    ,      if (eng) return; OPENSSL_add_all_algorithms_conf(); ERR_load_crypto_strings(); if (!(eng = ENGINE_by_id("gost"))) { printf("Engine gost doesn't exist"); return; } ENGINE_init(eng); ENGINE_set_default(eng, ENGINE_METHOD_ALL); } const EVP_MD * EVP_gostR3411_2012_256() { krb5int_init_gost(); return EVP_get_digestbynid(NID_id_GostR3411_2012_256); } const EVP_MD * EVP_gostR3411_2012_512() { krb5int_init_gost(); return EVP_get_digestbynid(NID_id_GostR3411_2012_512); }
          
          





  2. After adding the auxiliary library, it will be necessary to declare its existence in the Makefile and write out its dependencies on files. To do this, add the following:







     #    src/lib/crypto/openssl/Makefile.in      : STLIBOBJS=\ hmac.o \ ... stubs.o \ gost_helper.o OBJS=\ $(OUTPRE)hmac.$(OBJEXT) \ ... $(OUTPRE)stubs.$(OBJEXT) \ $(OUTPRE)gost_helper$(OBJEXT) SRCS=\ $(srcdir)/hmac.c \ ... $(srcdir)/stubs.c \ $(srcdir)/gost_helper.c #    src/lib/crypto/openssl/deps ,         : gost_helper.so gost_helper.po $(OUTPRE)gost_helper.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ gost_helper.c gost_helper.h
          
          





  3. It is worth noting that in the future, when connecting this library, we will need to add the dependencies of some files on the header of this library. This is done very simply - the goal is found, where you need to add the dependency in the deps file and the dependence on rel / path / to / gost_helper.h is recorded . For example, in src / lib / crypto / openssl / hash_provider / deps you would need to add the following:







     hash_evp.so hash_evp.po $(OUTPRE)hash_evp.$(OBJEXT): \ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \ ... $(srcdir)/../gost_helper.h hash_evp.c
          
          





    In order to save space in the article and to stretch your brains, I will no longer pay attention to it: be careful and careful!







  4. Now we add the hash function implementations, all of their implementations are in src / lib / crypto / openssl / hash_provider / hash_evp.c . There you will need to add the following:







     //     deps !!!          #include "crypto_int.h" #include "gost_helper.h" #include <openssl/evp.h> ... static krb5_error_code hash_sha384(const krb5_crypto_iov *data, size_t num_data, krb5_data *output) { return hash_evp(EVP_sha384(), data, num_data, output); } static krb5_error_code hash_stribog256(const krb5_crypto_iov *data, size_t num_data, krb5_data *output) { return hash_evp(EVP_gostR3411_2012_256(), data, num_data, output); } static krb5_error_code hash_stribog512(const krb5_crypto_iov *data, size_t num_data, krb5_data *output) { return hash_evp(EVP_gostR3411_2012_512(), data, num_data, output); } //    /*   --    --      --        --     */ const struct krb5_hash_provider krb5int_hash_sha384 = { "SHA-384", 48, 128, hash_sha384 }; const struct krb5_hash_provider krb5int_hash_stribog256 = { "GOSTR34.11-2012-256", 32, 64, hash_stribog256 }; const struct krb5_hash_provider krb5int_hash_stribog512 = { "GOSTR34.11-2012-512", 64, 64, hash_stribog512 };
          
          





  5. Now we need to declare these contexts throughout the library. To do this, create their description in the file src / lib / crypto / krb / crypto_int.h .







     //  ,    ,        ,     krb5_hash_provider   -     : ... struct krb5_hash_provider { char hash_name[32]; //  8 size_t hashsize, blocksize; krb5_error_code (*hash)(const krb5_crypto_iov *data, size_t num_data, krb5_data *output); }; ... extern const struct krb5_hash_provider krb5int_hash_sha384; extern const struct krb5_hash_provider krb5int_hash_stribog256; extern const struct krb5_hash_provider krb5int_hash_stribog512; ...
          
          





  6. We’ll declare the identifier of the Stribog and AES bundle, introducing macros that we will call CKSUMTYPE_STRIBOG_256_AES256 , CKSUMTYPE_STRIBOG_512_AES256 , ENCTYPE_AES256_CTS_STRIBOG_256 , ENCTYPE_AES256_CTS_STRIBOG . They must be declared in the header file template src / include / krb5 / krb5.hin . It will look something like this:







     #define ENCTYPE_ARCFOUR_HMAC_EXP 0x0018 /**< RFC 4757 */ #define ENCTYPE_CAMELLIA128_CTS_CMAC 0x0019 /**< RFC 6803 */ #define ENCTYPE_CAMELLIA256_CTS_CMAC 0x001a /**< RFC 6803 */ #define ENCTYPE_AES256_CTS_STRIBOG_256 0x001b /**< NO RFC */ #define ENCTYPE_AES256_CTS_STRIBOG_512 0x001c /**< NO RFC */ #define ENCTYPE_UNKNOWN 0x01ff ... #define CKSUMTYPE_CMAC_CAMELLIA256 0x0012 /**< RFC 6803 */ #define CKSUMTYPE_MD5_HMAC_ARCFOUR -137 /* Microsoft netlogon */ #define CKSUMTYPE_HMAC_MD5_ARCFOUR -138 /**< RFC 4757 */ #define CKSUMTYPE_STRIBOG_256_AES256 -139 /**< NO RFC */ #define CKSUMTYPE_STRIBOG_512_AES256 -140 /**< NO RFC */
          
          





  7. Now you need to add two bundles of the hashing and encryption functions and the encryption and hashing functions. Why two, if they are equivalent, you ask? Answer: I do not know, or is it a historical crutch or a way to optimize. Nevertheless, let's add new structures to the necessary files:







     //   src/lib/crypto/krb/cksumtypes.c          /*   --   --     --       --    --       --       --    "  "   --     --     --   */ const struct krb5_cksumtypes krb5int_cksumtypes_list[] = { ... { CKSUMTYPE_STRIBOG_256_AES256, "stribog-256-aes256", { 0 }, "STRIBOG256 AES256 key", &krb5int_enc_aes256, &krb5int_hash_stribog256, krb5int_etm_checksum, NULL, 64, 32, 0 }, { CKSUMTYPE_STRIBOG_512_AES256, "stribog-512-aes256", { 0 }, "STRIBOG512 AES256 key", &krb5int_enc_aes256, &krb5int_hash_stribog512, krb5int_etm_checksum, NULL, 64, 64, 0 }, }; //   src/lib/crypto/krb/etypes.c         : /*   --   --   --   --   --      --      --           --   ,     --    ,       --    ,       --         --        --    --   */ const struct krb5_keytypes krb5int_enctypes_list[] = { ... { ENCTYPE_AES256_CTS_STRIBOG_256, "aes256-cts-stribog-256", { "aes256-stribog256" }, "AES-256 CTS mode with 256-bit stribog", &krb5int_enc_aes256, &krb5int_hash_stribog256, 16, krb5int_aes2_crypto_length, krb5int_etm_encrypt, krb5int_etm_decrypt, krb5int_aes2_string_to_key, k5_rand2key_direct, krb5int_aes2_prf, CKSUMTYPE_STRIBOG_256_AES256, 0 /*flags*/, 256 }, { ENCTYPE_AES256_CTS_STRIBOG_512, "aes256-cts-stribog-512", { "aes256-stribog512" }, "AES-256 CTS mode with 512-bit stribog", &krb5int_enc_aes256, &krb5int_hash_stribog512, 16, krb5int_aes2_crypto_length, krb5int_etm_encrypt, krb5int_etm_decrypt, krb5int_aes2_string_to_key, k5_rand2key_direct, krb5int_aes2_prf, CKSUMTYPE_STRIBOG_512_AES256, 0 /*flags*/, 256 }, };
          
          





  8. It would seem that all, but no! Then there are problems that will be visible only during the debugging process, some of them could be avoided by indicating, for example, other pointers to functions in the above structures, but we will go in a more complicated way to show what else you may have to fix along the way. I learned about all these problems only using the debugger:







     //   src/lib/crypto/openssl/hmac.c    map_digest --    -   .         .  : #include "crypto_int.h" #include "gost_helper.h" #include <openssl/hmac.h> #include <openssl/evp.h> static const EVP_MD * map_digest(const struct krb5_hash_provider *hash) { if (!strncmp(hash->hash_name, "SHA1",4)) return EVP_sha1(); ... else if (!strncmp(hash->hash_name, "GOSTR34.11-2012-256", 19)) return EVP_gostR3411_2012_256(); else if (!strncmp(hash->hash_name, "GOSTR34.11-2012-512", 19)) return EVP_gostR3411_2012_512(); else return NULL; } //   src/lib/crypto/openssl/pbkdf2.c  krb5int_pbkdf2_hmac,        : krb5_error_code krb5int_pbkdf2_hmac(const struct krb5_hash_provider *hash, const krb5_data *out, unsigned long count, const krb5_data *pass, const krb5_data *salt) { const EVP_MD *md = NULL; /* Get the message digest handle corresponding to the hash. */ if (hash == &krb5int_hash_sha1) md = EVP_sha1(); ... else if (hash == &krb5int_hash_stribog256) md = EVP_gostR3411_2012_256(); else if (hash == &krb5int_hash_stribog512) md = EVP_gostR3411_2012_512(); ... return 0; } //   src/lib/krb5/krb/init_ctx.c      ,   : static krb5_enctype default_enctype_list[] = { ... ENCTYPE_AES256_CTS_STRIBOG_256, ENCTYPE_AES256_CTS_STRIBOG_512, 0 };
          
          







After all these changes, you can check the operation of the algorithm. We will collect everything that we have done.







 autoconf ./configure --with-crypto-impl=openssl #     openssl make sudo make install
      
      





Now let's start checking. To do this, let's put the forced use of the algorithms we have implemented into the Kerberos configuration files. Do the following:







  1. Stop krb5kdc:







     service krb5-kdc stop
          
          





  2. We’ll fix the kdc.conf configuration file (I have it /usr/local/var/krb5kdc/kdc.conf). Set forced hashing using the newly introduced algorithm:







     [realms] AKTIV-TEST.RU = { master_key_type = aes256-stribog512 supported_enctypes = aes256-stribog512:normal default_tgs_enctypes = aes256-stribog512 default_tkt_enctypes = aes256-stribog512 permitted_enctypes = aes256-stribog512 } #       256 
          
          





  3. Similar changes in the configuration file of the entire protocol krb5.conf (I have it at /etc/krb5.conf):







     [libdefaults] supported_enctypes = aes256-stribog512:normal default_tgs_enctypes = aes256-stribog512 default_tkt_enctypes = aes256-stribog512 permitted_enctypes = aes256-stribog512 #      256 
          
          





  4. Next, run krb5kdc since If master_key changes, you may have to create the principals database again using krb5_newrealm.







  5. After that, we create all the necessary principals and you can start work. Try authenticating with kinit.







  6. We verify that the hashing occurs according to the specified algorithm with

    using klist -e.









If the service does not start, then it can be started from under the root using src / kdc / krb5kdc . If everything started, it went smoothly - congratulations! Otherwise - alas, I do not offer a panacea for all problems, but only offer a "small" instruction containing the basic steps that you will have to take in order to implement the new algorithm in Kerberos. And if nothing works out for you - pick up gdb and see where what goes wrong. I can only give you some tips:







  1. you can build a project in debug mode if you pass it to ./configure CFLAGS = "-g -O0";
  2. krb5kdc can be launched in the background using the -n flag;
  3. I hope this doesn’t come to that (although I still came up with the implementation of asymmetric algorithms) - you may need openssl debugging symbols - either install them from the repository or install openssl from source with debugging symbols;
  4. the same goes for gost engine.


The idea of ​​this set of tips should be enough for you to save yourself from wasting time looking for the β€œunknown”.







Encryption algorithm implementation



In this section I will show how you can add your own data encryption algorithm in Kerberos, and we will try to create a bunch of Magma and Stribog, added in the last section. Here I already assume that the small gost_helper library has already been added in the last section. Well, stretch your fingers and proceed:







  1. First, we describe the algorithm in our library libk5crypto by describing them in the header file src / lib / crypto / krb / crypto_int.h .







     ... extern const struct krb5_enc_provider krb5int_enc_camellia256; extern const struct krb5_enc_provider krb5int_enc_gost89; ...
          
          





  2. In the src / lib / crypto / openssl / enc_provider directory, add the source code gost.c, which contains the implementation of all the necessary algorithms (I took the source of the des algorithm as the basis). It is important to note that we only implement cbc encryption mode, so for self-testing you can take any other encryption mode and add it:







     #include "crypto_int.h" #include "gost_helper.h" #include <openssl/evp.h> #define BLOCK_SIZE 8 static krb5_error_code krb5int_gost_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { int ret, olen = BLOCK_SIZE; unsigned char iblock[BLOCK_SIZE], oblock[BLOCK_SIZE]; struct iov_cursor cursor; EVP_CIPHER_CTX *ctx; //          ,   krb5int_gost_encrypt,    : krb5int_init_gost(); ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) return ENOMEM; ret = EVP_EncryptInit_ex(ctx, EVP_get_cipherbynid(NID_gost89_cbc), NULL, key->keyblock.contents, (ivec) ? (unsigned char*)ivec->data : NULL); if (!ret) { EVP_CIPHER_CTX_free(ctx); return KRB5_CRYPTO_INTERNAL; } EVP_CIPHER_CTX_set_padding(ctx,0); k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE); while (k5_iov_cursor_get(&cursor, iblock)) { ret = EVP_EncryptUpdate(ctx, oblock, &olen, iblock, BLOCK_SIZE); if (!ret) break; k5_iov_cursor_put(&cursor, oblock); } if (ivec != NULL) memcpy(ivec->data, oblock, BLOCK_SIZE); EVP_CIPHER_CTX_free(ctx); zap(iblock, sizeof(iblock)); zap(oblock, sizeof(oblock)); if (ret != 1) return KRB5_CRYPTO_INTERNAL; return 0; } static krb5_error_code krb5int_gost_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { int ret, olen = BLOCK_SIZE; unsigned char iblock[BLOCK_SIZE], oblock[BLOCK_SIZE]; struct iov_cursor cursor; EVP_CIPHER_CTX *ctx; krb5int_init_gost(); ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) return ENOMEM; ret = EVP_DecryptInit_ex(ctx, EVP_get_cipherbynid(NID_gost89_cbc), NULL, key->keyblock.contents, (ivec) ? (unsigned char*)ivec->data : NULL); if (!ret) { EVP_CIPHER_CTX_free(ctx); return KRB5_CRYPTO_INTERNAL; } EVP_CIPHER_CTX_set_padding(ctx,0); k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE); while (k5_iov_cursor_get(&cursor, iblock)) { ret = EVP_DecryptUpdate(ctx, oblock, &olen, (unsigned char *)iblock, BLOCK_SIZE); if (!ret) break; k5_iov_cursor_put(&cursor, oblock); } if (ivec != NULL) memcpy(ivec->data, iblock, BLOCK_SIZE); EVP_CIPHER_CTX_free(ctx); zap(iblock, sizeof(iblock)); zap(oblock, sizeof(oblock)); if (ret != 1) return KRB5_CRYPTO_INTERNAL; return 0; } static krb5_error_code krb5int_gost_init_state (const krb5_keyblock *key, krb5_keyusage usage, krb5_data *state) { state->length = 8; state->data = (void *) malloc(8); if (state->data == NULL) return ENOMEM; memset(state->data, 0, state->length); return 0; } static void krb5int_gost_free_state(krb5_data *state) { free(state->data); *state = empty_data(); } /*   --  ,    -- -       --        --      --      --    cbc-mac checksum,      , ..       --       --         */ const struct krb5_enc_provider krb5int_enc_gost89 = { BLOCK_SIZE, 32, 32, krb5int_gost_encrypt, krb5int_gost_decrypt, NULL, krb5int_gost_init_state, krb5int_gost_free_state };
          
          





  3. In the src / lib / crypto / openssl / enc_provider / Makefile.in template, we indicate that a new source has appeared:







     STLIBOBJS= \ des.o \ ... gost.o OBJS= \ $(OUTPRE)des.$(OBJEXT) \ ... $(OUTPRE)gost$(OBJEXT) SRCS= \ $(srcdir)/des.c \ ... $(srcdir)/gost.c
          
          





  4. Do not forget about specifying dependencies in src / lib / crypto / openssl / enc_provider / deps :







     gost.so gost.po $(OUTPRE)gost.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../../krb/crypto_int.h \ $(srcdir)/../crypto_mod.h $(top_srcdir)/include/k5-buf.h \ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ $(top_srcdir)/include/socket-utils.h $(srcdir)/../gost_helper.h gost.c
          
          





  5. Add the bundle identifiers to src / include / krb5 / krb5.hin :







     ... #define ENCTYPE_AES256_CTS_STRIBOG_256 0x001b /**< NO RFC */ #define ENCTYPE_AES256_CTS_STRIBOG_512 0x001c /**< NO RFC */ #define ENCTYPE_GOST89_CBC_STRIBOG_256 0x001d /**< SOME GOST */ #define ENCTYPE_GOST89_CBC_STRIBOG_512 0x001e /**< SOME GOST */ #define ENCTYPE_UNKNOWN 0x01ff ... #define CKSUMTYPE_STRIBOG_256_AES256 -139 /**< NO RFC */ #define CKSUMTYPE_STRIBOG_512_AES256 -140 /**< NO RFC */ #define CKSUMTYPE_STRIBOG_256_GOST89 -141 /**< SOME GOST */ #define CKSUMTYPE_STRIBOG_512_GOST89 -142 /**< SOME GOST */
          
          





  6. Let us describe the structure of the hash and encryption function bundles, as in the last section:







     // src/lib/crypto/krb/cksumtypes.c const struct krb5_cksumtypes krb5int_cksumtypes_list[] = { ... { CKSUMTYPE_STRIBOG_256_GOST89, "stribog-256-gost89", { 0 }, "STRIBOG256 GOST89 key", &krb5int_enc_gost89, &krb5int_hash_stribog256, krb5int_dk_checksum, NULL, 64, 32, 0 }, { CKSUMTYPE_STRIBOG_512_GOST89, "stribog-512-gost89", { 0 }, "STRIBOG512 GOST89 key", &krb5int_enc_gost89, &krb5int_hash_stribog512, krb5int_dk_checksum, NULL, 64, 64, 0 }, }; // src/lib/crypto/krb/etypes.c //       ,         , ,      aes const struct krb5_keytypes krb5int_enctypes_list[] = { ... { ENCTYPE_GOST89_CBC_STRIBOG_256, "gost89-cbc-stribog-256", { "gost89-stribog256" }, "GOST 28147-89 CBC mode with 256-bit stribog", &krb5int_enc_gost89, &krb5int_hash_stribog256, 16, krb5int_dk_crypto_length, krb5int_dk_encrypt, krb5int_dk_decrypt, krb5int_dk_string_to_key, k5_rand2key_direct, krb5int_dk_prf, CKSUMTYPE_STRIBOG_256_GOST89, 0 /*flags*/, 256 }, { ENCTYPE_GOST89_CBC_STRIBOG_512, "gost89-cbc-stribog-512", { "gost89-stribog512" }, "GOST 28147-89 CBC mode with 512-bit stribog", &krb5int_enc_gost89, &krb5int_hash_stribog512, 16, krb5int_dk_crypto_length, krb5int_dk_encrypt, krb5int_dk_decrypt, krb5int_dk_string_to_key, k5_rand2key_direct, krb5int_dk_prf, CKSUMTYPE_STRIBOG_512_GOST89, 0 /*flags*/, 256 }, };
          
          





  7. Add to the list of default encryption modes in the src / lib / krb5 / krb / init_ctx.c file :







     static krb5_enctype default_enctype_list[] = { ... ENCTYPE_AES256_CTS_STRIBOG_256, ENCTYPE_AES256_CTS_STRIBOG_512, ENCTYPE_GOST89_CBC_STRIBOG_256, ENCTYPE_GOST89_CBC_STRIBOG_512, 0 };
          
          







After these manipulations, you can try to test the newly introduced algorithm. Testing takes place in the same way as in the last section . The name of the bundle can be taken in the form of an alias (for example, gost89-stribog512) or using the name of the algorithm itself (for example, gost89-cbc-stribog-512). I hope that everything works, otherwise do not forget about what I said earlier .







Adding a Digital Signature Algorithm



Hurrah! We proceed to the final section of this article and try to add our own electronic signature algorithm. Don’t be scared, adding it is easier than anything else, so let's get started as soon as possible ... Although no, wait a little remark to start.







Asymmetric encryption is a pretty heavyweight thing. – : , - . ...







Guys, do not open this topic. You are young, playful, everything is easy for you. . This is not Chikatilo and not even the archives of special services. It’s better not to climb here. Seriously, any of you will be sorry. Better close the topic and forget what was written here. , , β€” . The rest simply will not find.

Something like this. , , . : . , , , , , .







openssl ( )



, -, openssl rtengine. GOST, , .







  1. SDK rutoken sdk/openssl/rtengine/bin/ engine.







  2. librtpkcs11ecp.so.







  3. master OpenSC 8cf1e6f







  4. , openssl.cnf:







     #     openssl_conf = openssl_def ... #    # OpenSSL default section [openssl_def] engines = engine_section # Engine section [engine_section] gost = gost_section rtengine = rtengine_section # Engine gost section [gost_section] engine_id = gost dynamic_path = /usr/lib/x86_64-linux-gnu/engines-1.1/gost.so default_algorithms = ALL # Engine rtengine section [rtengine_section] engine_id = rtengine dynamic_path = /path/to/engine/librtengine.so MODULE_PATH = /path/to/module/librtpkcs11ecp.so RAND_TOKEN = pkcs11:manufacturer=Aktiv%20Co.;model=Rutoken%20ECP default_algorithms = CIPHERS, DIGEST, PKEY, RAND
          
          







engine .







, KDC



kerberos . , .







  1. , , , KDC:







     openssl genpkey -engine gost -algorithm gost2012_256 -pkeyopt paramset:B -out CA_key.pem #    openssl req -engine gost -key CA_key.pem -new -x509 -out CA_cert.pem #    openssl genpkey -engine gost -algorithm gost2012_256 -pkeyopt paramset:B -out KDC_key.pem #   KDC openssl req -engine gost -new -out KDC.req -key ./KDC_key.pem #      KDC # !!!     pkinit_extensions    REALM=AKTIV-TEST.RU; export REALM #   KDC CLIENT=127.0.0.1; export CLIENT #   ,     (    KDC).    ,   localhost openssl x509 -engine gost -req -in ./KDC.req -CAkey ./CA_key.pem -CA ./CA_cert.pem -out ./KDC.pem -extfile ./pkinit_extensions -extensions kdc_cert -CAcreateserial #    KDC. sudo cp ./KDC.pem ./KDC_key.pem ./CA_cert.pem /var/lib/krb5kdc #        kdc.
          
          





  2. kdc, , :







     [kdcdefaults] ... pkinit_identity = FILE:/var/lib/krb5kdc/KDC.pem,/var/lib/krb5kdc/KDC_key.pem pkinit_anchors = FILE:/var/lib/krb5kdc/CA_cert.pem
          
          







[libdefaults]

spake_preauth_groups = edwards25519







 3.      : ```bash sudo kadmin.local kadmin.local$: modprinc +requires_preauth user
      
      





  1. . , , : :







    1. , KDC:







       openssl genpkey -engine gost -algorithm gost2012_256 -pkeyopt paramset:B -out client_key.pem #    openssl req -engine gost -new -out client.req -key ./client_key.pem #      
            
            





    2. :







       pkcs11-tool --module /path/to/module/librtpkcs11ecp.so --keypairgen --key-type GOSTR3410-2012-256:B -l --id 45 #             id=45 openssl req -engine rtengine -new -key="pkcs11:id=E" -keyform engine -out client.req #         . E -- ascii  45
            
            







  2. :







     REALM=AKTIV-TEST.RU; export REALM #   CLIENT=user; export CLIENT #  ,     openssl x509 -engine gost -CAkey ./CA_key.pem -CA ./CA_cert.pem -req -in ./client.req -extensions client_cert -extfile ./pkinit_extensions -out client.pem openssl x509 -engine gost -in client.pem -out client.crt -outform DER #     PEM   CRT
          
          





  3. : , – :







     sudo cp ./client_key.pem client.pem /etc/krb5 #   pkcs11-tool --module /usr/lib/librtpkcs11ecp.so -l -y cert -w ./client.crt --id 45 #   (     id,   )
          
          





  4. ( /etc/krb5.conf):







     [libdefaults] ... pkinit_anchors = FILE:/var/lib/krb5kdc/CA_cert.pem #     pkinit_identities = FILE:/etc/krb5/client.pem,/etc/krb5/client_key.pem #     #pkinit_identities = PKCS11:/usr/lib/librtpkcs11ecp.so
          
          







, . ! .









, – - 2 ! src/plugins/preauth/pkinit/pkcs11.h src/plugins/preauth/pkinit/pkinit_crypto_openssl.c







  1. pkcs11.h . – , , . ( ). sdk/pkcs11/include/rtpkcs11t.h . :







     ... #define CKK_TWOFISH (0x21) #define CKK_GOSTR3410 (0x30) #define CKK_GOSTR3411 (0x31) #define CKK_GOST28147 (0x32) #define CKK_VENDOR_DEFINED (1UL << 31) // A mask for new GOST algorithms. // For details visit https://tc26.ru/standarts/perevody/guidelines-the-pkcs-11-extensions-for-implementing-the-gost-r-34-10-2012-and-gost-r-34-11-2012-russian-standards-.html #define NSSCK_VENDOR_PKCS11_RU_TEAM (CKK_VENDOR_DEFINED | 0x54321000) #define CK_VENDOR_PKCS11_RU_TEAM_TK26 NSSCK_VENDOR_PKCS11_RU_TEAM #define CKK_GOSTR3410_512 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x003) ... #define CKM_AES_MAC_GENERAL (0x1084) #define CKM_AES_CBC_PAD (0x1085) #define CKM_GOSTR3410_KEY_PAIR_GEN (0x1200UL) #define CKM_GOSTR3410 (0x1201UL) #define CKM_GOSTR3410_WITH_GOSTR3411 (0x1202UL) #define CKM_GOSTR3410_KEY_WRAP (0x1203UL) #define CKM_GOSTR3410_DERIVE (0x1204UL) #define CKM_GOSTR3410_512_KEY_PAIR_GEN (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x005) #define CKM_GOSTR3410_512 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x006) #define CKM_GOSTR3410_12_DERIVE (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x007) #define CKM_GOSTR3410_WITH_GOSTR3411_12_256 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x008) #define CKM_GOSTR3410_WITH_GOSTR3411_12_512 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x009) #define CKM_GOSTR3411 (0x1210UL) #define CKM_GOSTR3411_HMAC (0x1211UL) #define CKM_GOSTR3411_12_256 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x012) #define CKM_GOSTR3411_12_512 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x013) #define CKM_GOSTR3411_12_256_HMAC (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x014) #define CKM_GOSTR3411_12_512_HMAC (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x015) #define CKM_GOST28147_KEY_GEN (0x1220UL) #define CKM_GOST28147_ECB (0x1221UL) #define CKM_GOST28147 (0x1222UL) #define CKM_GOST28147_MAC (0x1223UL) #define CKM_GOST28147_KEY_WRAP (0x1224UL)
          
          





    , .







  2. pkinit_crypto_openssl.c , , . get_key, .. - :







     #include <dirent.h> #include <arpa/inet.h> #include <openssl/engine.h> static ENGINE *eng = NULL; krb5int_init_engines() { if (eng) return; OPENSSL_add_all_algorithms_conf(); ERR_load_crypto_strings(); if (!(eng = ENGINE_by_id("rtengine"))) { printf("Engine rtengine doesn't exist"); return; } ENGINE_init(eng); ENGINE_set_default(eng, ENGINE_METHOD_ALL); if (!(eng = ENGINE_by_id("gost"))) { printf("Engine gost doesn't exist"); return; } ENGINE_init(eng); ENGINE_set_default(eng, ENGINE_METHOD_ALL); } ... get_key(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, char *filename, const char *fsname, EVP_PKEY **retkey, const char *password) { ... krb5_error_code retval; krb5int_init_engines(); ... } ... int pkinit_openssl_init() { /* Initialize OpenSSL. */ ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); krb5int_init_engines(); return 0; }
          
          





  3. . – RSA , sha1. , . , , . , .. RSA :







     //       krb5_error_code pkinit_find_private_key(pkinit_identity_crypto_context id_cryptoctx, CK_ATTRIBUTE_TYPE usage, CK_OBJECT_HANDLE *objp) { ... true_false = TRUE; attrs[nattrs].type = usage; attrs[nattrs].pValue = &true_false; attrs[nattrs].ulValueLen = sizeof true_false; nattrs++; #endif // keytype = CKK_RSA; // attrs[nattrs].type = CKA_KEY_TYPE; // attrs[nattrs].pValue = &keytype; // attrs[nattrs].ulValueLen = sizeof keytype; // nattrs++; ... } //             : static int ckk_key_to_nid(CK_KEY_TYPE type) { switch(type){ case CKK_GOSTR3410: return NID_id_GostR3410_2012_256; case CKK_GOSTR3410_512: return NID_id_GostR3410_2012_512; default: return NID_rsa; } } // ,     ,     : static int pkinit_get_pkey_type(krb5_context context, pkinit_identity_crypto_context id_cryptoctx) { CK_OBJECT_HANDLE obj; CK_ATTRIBUTE attrs[1]; CK_KEY_TYPE key_type; int r; //  : if (pkinit_open_session(context, id_cryptoctx)) { pkiDebug("can't open pkcs11 session\n"); return NID_rsa; } //   : if (pkinit_find_private_key(id_cryptoctx, CKA_SIGN, &obj)) { return NID_rsa; } //   : attrs[0].type = CKA_KEY_TYPE; attrs[0].pValue = &key_type; attrs[0].ulValueLen = sizeof (key_type); if ((r = id_cryptoctx->p11->C_GetAttributeValue(id_cryptoctx->session, obj, attrs, 1)) != CKR_OK) { pkiDebug("C_GetAttributeValue: %s\n Used RSA\n", pkinit_pkcs11_code_to_text(r)); return NID_rsa; } //   : return ckk_key_to_nid(key_type); } // ,        ,     : static int pkey_to_digest_nid(const EVP_PKEY* const pkey) { switch (EVP_PKEY_id(pkey)) { case NID_id_GostR3410_2012_256: return NID_id_GostR3411_2012_256; case NID_id_GostR3410_2012_512: return NID_id_GostR3411_2012_512; case NID_id_GostR3410_2001: return NID_id_GostR3411_2012_256; default: return NID_sha1; } } // ,       : static int get_digest_nid(krb5_context context, const pkinit_identity_crypto_context id_cryptctx) { int nid; //    (   ),   NID ,    if (id_cryptctx->my_key) { nid = EVP_PKEY_id(id_cryptctx->my_key); } else { nid = pkinit_get_pkey_type(context, id_cryptctx); } switch (nid) { case NID_id_GostR3410_2012_256: return NID_id_GostR3411_2012_256; case NID_id_GostR3410_2012_512: return NID_id_GostR3411_2012_512; case NID_id_GostR3410_2001: return NID_id_GostR3411_2012_256; default: return NID_sha1; } } // ,     : static int get_alg_nid(krb5_context context, const pkinit_identity_crypto_context id_cryptctx) { int nid; if (id_cryptctx->my_key) { nid = EVP_PKEY_id(id_cryptctx->my_key); } else { nid = pkinit_get_pkey_type(context, id_cryptctx); } switch (nid) { case NID_id_GostR3410_2012_256: return NID_id_tc26_signwithdigest_gost3410_2012_256; case NID_id_GostR3410_2012_512: return NID_id_tc26_signwithdigest_gost3410_2012_512; case NID_id_GostR3410_2001: return NID_id_tc26_signwithdigest_gost3410_2012_256; default: return NID_sha1WithRSAEncryption; } } //    : static CK_MECHANISM_TYPE get_mech_type(krb5_context context, const pkinit_identity_crypto_context id_cryptctx) { int nid; if (id_cryptctx->my_key) { nid = EVP_PKEY_id(id_cryptctx->my_key); } else { nid = pkinit_get_pkey_type(context, id_cryptctx); } switch (nid) { case NID_id_GostR3410_2012_256: return CKM_GOSTR3410_WITH_GOSTR3411_12_256; case NID_id_GostR3410_2012_512: return CKM_GOSTR3410_WITH_GOSTR3411_12_512; case NID_id_GostR3410_2001: return CKM_GOSTR3410_WITH_GOSTR3411_12_256; default: return CKM_RSA_PKCS; } }
          
          





  4. cms_signeddata_create create_signature :







     krb5_error_code cms_signeddata_create(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, int cms_msg_type, int include_certchain, unsigned char *data, unsigned int data_len, unsigned char **signed_data, unsigned int *signed_data_len) { ... /* Set digest algs */ p7si->digest_alg->algorithm = OBJ_nid2obj( get_digest_nid(context, id_cryptoctx)); ... p7si->digest_enc_alg->algorithm = OBJ_nid2obj(get_alg_nid(context, id_cryptoctx)); ... EVP_DigestInit_ex(ctx, EVP_get_digestbynid(get_digest_nid(context, id_cryptoctx)), NULL); ... alen = (unsigned int )ASN1_item_i2d((ASN1_VALUE *) sk, &abuf, ASN1_ITEM_rptr(PKCS7_ATTR_SIGN)); ... //   ,      (    ): if (id_cryptoctx->pkcs11_method == 1 && get_digest_nid(context, id_cryptoctx) == NID_sha1) { } static krb5_error_code create_signature(unsigned char **sig, unsigned int *sig_len, unsigned char *data, unsigned int data_len, EVP_PKEY *pkey) { ... EVP_SignInit(ctx, EVP_get_digestbynid(pkey_to_digest_nid(pkey))); ... } //       : static krb5_error_code pkinit_sign_data_pkcs11(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **sig, unsigned int *sig_len) { ... mech.mechanism = get_mech_type(context, id_cryptoctx); mech.pParameter = NULL; mech.ulParameterLen = 0; ... }
          
          







, ( , , ):







 sudo kinit user
      
      





user, , .







, .








All Articles