category-group: security
layer(s): 2, 7, 10

header file(s): z_crypt.h, z_crypt_header.h, z_uacct.h

synopsis.
This group deals with security topics - primarily encryption. Security is about protection, and protection of data comprises the bulk of the security group. It also includes checking whether a user (or possibly a software object) has access permissions. This is done via the "user account" component-object (uacct_o), which gives a program a tool to manage user accounts.

In the Z Directory, the interfaces for encrypting and decrypting operate on strings. Since the string object can handle binary data, this means binary (non-text) files can be encrypted using the string class.

classes in this group: crypto_o, filecrypt_header_o, iocrypt_o, uacct_o, no_crypt_o,
                                      rot13_crypt_o, coolhash_crypt_o, nile_crypt_o, vettra1_crypt_o,
                                      chambran_crypt_o, blowfish_crypt_o, twofish_crypt_o, DES_crypt_o,
                                      pair_crypt_o, Rijndael_crypt_o

description.
All encryption objects here conform a standardized pattern. Their usage is all the same, with a few exceptions, which will be duly noted. Using the encryption components is a straightforward process:

  • create a crypto object (instantiate it);
  • set up a string object, in binary mode, to contain the data to encrypt;
  • set the crypto password (some don't require one);
  • encrypt the data block.
  • decrypt the data block. if a different crypto object is used, it obviously must have the password set (pair_crypt has some different behariour here).
The set of encryptor objects form a 2-level tree. The abstract class crypto_o defines up the interface for all encryption algorithms found in the Z Directory. Subclasses from crypto_o provide the components for doing actual encryption and decryption:
rot13_crypt_o ........: "rot-13", a simple bit rotation
coolhash_crypt_o .....: unknown origin encryptor (BROKEN)
nile_crypt_o .........: keyless cipher
vettra1_crypt_o ......: our [first] encryptor
DES_crypt_o ..........: standard DES algorithm
chambran_crypt_o .....: encryptor algorithm by Bill Chambers (BROKEN)
blowfish_crypt_o .....: implementation of blowfish (by Bruce Schneier)
twofish_crypt_o  .....: implementation of twofish (also by B. Schneier)
pair_crypt_o .........: RSA
Rijndael_crypt_o .....: Rijndael (AES winner)
Although the application interface, defined in the crypto_o class, is the same for each cipher, often there are specific requirements for using the cipher that the user needs to be aware about:
  • The output size of some methods might be larger than the input size. That is, one bye of data may generate 2 or 4 bytes of encrypted output. For example:
    nile_crypt_o: 1 input byte creates 2 output bytes
  • Some methods may require the input block to be in n-byte multiples.
    For example:

    chambran_crypt_o: input and output must be in units of 4 bytes (that is, the numer of bytes to encrypt should be 4, 8, 12, ... 4200, 4204, 4208, .. etc)

    DES_crypt_o: input and output sizes should be in multiples of 64 bytes.

    pair_crypt_o: input and output sizes should be in units of 16 bytes.

    Rijndael_crypt_o: you have a choice of these 3 input and output sizes: 16, 24, or 32 bytes.

  • Some methods add padding to the decrypted result. This is so with pair_crypt (We're not sure why) - When a block is decrypted, the length the the string sometimes (not always, and not the by same amount) is a bit longer, typically 3-8 bytes.
  • The password may need to be of a specific length. In particular, with Rijndael_crypt_o, the key must be 16, 24, or 32 bytes long.

  • The password-key usage can get more complicated. See the page for pair_crypt_o for an explanation of RSA key generation and usage.
  • There may be additional usage requirements. Some ciphers may require a 1-time initialization performed before using it. Rijndael_crypt_o needs a call to 'gen_key()'. Some ciphers require setting a second password (often called "the salt"). There can be other nuances, specific to the cipher.
  • If you want to encrypt some text, don't want to bother with a password, you don't consider the contents that important, and you might possibly reuse the same data space without having to resize it, you may consider rot13_crypt . If you still don't want to remember a password, and you don't mind allocating an output-target space twice as big as the data, you can use nile_crypt . For more secure encryption, you can step it up to vettra1_crypt (output size is 1:1). If the data is sensitive, DES_crypt may suffice. However the infinitely popular DES has been broken a long time ago. Better to use blowfish (blowfish_crypt_o). However, Bruce Schneier, the author of blowfish, realized he could make a much stronger cipher, so later he created twofish. Many consider these two ciphers very strong. If your data to encrypt is really important and you don't want anybody else getting to it, or if you're worried about a fedreral agency trying to pry into your data using a team of cryptographers, use pair_crypt (an implementation of RSA) or Rijndael_crypt_o . Keep in mind that for RSA you'll have to manage two [ugly, long machine-generated] passwords.

    The pages for each of our encryption classes are fairly brief, as their member functions are almost all the same. In many cases, the signiture set of the member functions are exactly the same. Those pages will focus on explaining the nuances for the particular encryptor.

    The current roster (as of 2017) of the Z Directory encryption algorithms:

    name algorithm key type key size description
    nocrypt_o [none] none n.a. this does absolutely no encryption. It exists for testing and learning the Z Directory API. The output is cleartext, the same as the input. You might find use for it to create an implementation of the unix 'cat' program, or to copy bytes from A to B.
    rot13_crypt_o ROT-13 none n.a. This is the most elementary of encryption methods. It should not be used for any serious data encryption. It uses no key, and simply maps one letter to another. This is done by adding an offset to a value (ie, 'A' -> 'G', 'B' -> 'H', 'C' -> 'I", etc). ROT-13 is also known as the caeser cipher. It may dissuade a person with no knowledge of cryptography, but certainly not the determined hacker.
    coolhash_crypt_o Cool Hash none any; but internally, 16 bits This encryption method, named after contributor Rob McCool (our thanks to the Regents of the University of California). It is a keyless, and I/O is 1:1 (1 byte in makes 1 output byte). Though more complex than ROT-13, this is a weak encryptor.

    warning 1: this can only be used for text data, eg, numbers and letters. Also, the key gets distilled to a 4-byte u_long. Hence, this algorithm is fairly useless as a general-purpose encryptor.

    warning 2: As of 2017, coolhash cipher is broken: the decrypted output is garbage. It does not work. Don't use it (until further notice).

    nile_crypt_o custom algorithm none n.a. this encrypts data by performing a series of transformations using the data block itself as the password. Because of this, we consider nile encryption to be a weak encryptor. The encrypted block is double the size of the data block.
    vettra1_crypt_o Vettrasoft's custom cipher symmetric 1 .. 4,095 an encryption method based on our own ideas. This was created at Vettrasoft in 1984. The proprietary algorithm works in a very different way from most any other cipher, and we keep this secret. The "1" in the class name designates that it is Vettrasoft's first encryption algorithm. We have plans for a far more complex, stronger cipher algorithm (TBA). The vettra-1 cipher should be considered to be of medium-grade quality. It has not, as of this writing (2017) been examined to assess its quality, and (as far as we know) no attempts to hack or crack it have been done. . Prior to 2017, the Achilles Heel of this encryptor was its password: it was only 1 byte long! Although you could supply a password of any size, internally it was "boiled down" to 1 byte. At the time it was created, the cipher was simply an experiment. With the password problem, it was intensely vulnerable to brute-force attack and thus clearly of no use as a true encryptor. This was remedied in January 2017. Now you can use a password of any length up to 4,095 bytes (the longer, the better).
    chambran_crypt_o a cipher by Bill Chambers symmetric [?] Bill Chambers calls this a 'cryptographic pseudorandom number generator', which he created in 1995 (with some parts by Bob Jenkins). We don't have much info on this. it is a block cipher with S-boxes and other modern goodies.
    Bill pointed out that cipher algorithms and random number generators have much in common.

    The data blocks must be a multiple of 4, in order to get correct round-trip encryption-decryption; otherwise, there may be [up to 3] "extra" garbage bytes at the tail of the decrypted block.

    warning: as of this writing (2017), this class has recieved very little work and no testing. It is almost a certainty that it is broken. Don't use it! (until further notice).

    DES_crypt_o Data Encryption Standard symmetric 28 - 64 bytes DES is a famous encryption method, in wide use in the 1970's and 1980's. In this implementation, the key should be 64 bytes long, although it can be less. However there is a minimum size: 28 bytes. If you provide a key longer than 64 bytes, the excess is discarded.
    This encryptor should not be used on very short blocks of data (eg, under 80 bytes), and in fact, will not work on input 64 bytes or less. In order for the class to work on short data, we cipher it with other cryptos.
    DES was mandated in 1975 by Federal Information Processing 40FR12134.
    blowfish_crypt_o blowfish symmetric key block cipher [1-56] bytes According to some, blowfish is "slowly gaining acceptance as a strong encryption algorithm" (www.netaction.org/encrypt/guide.html)
    The blowfish algorithm was created in 1993 by Bruce Schneier. It was intended to be an alternative to DES. Although at the time considered quite strong, it has been superceded by twofish (by the same author).
    pair_crypt_o RSA asymmetric key (unsure) RSA is one of the strongest encryptors created to date. It has some quirks: you cannot supply your own password. Rather, one needs to generate a password pair. Also, the decrypted output comes in blocks of 11 bytes, so often the output string has a few bytes extra. With this you may want to keep track of the original (input) data block's length. . Here is a sample key:
    162AD5828539A3EE4E0ABB#4AB4CA7D38A434B5C1ED
    You need to call pair_crypt_o::generate_keys() to create a set of keys (1 public, 1 private).
    Rijndael_crypt_o Rijndael ("AES") symmetric block cipher 16/24/32 bytes The name Rijndael is often confused with AES, or Advanced Encryption Standard. Briefly, AES was a contest made by NIST, and Rijndael was the winner, in 2001. The algorithm was created in 1998. . The length of the Rijndael key must be specific: 128, 192, or 256 bits (16, 24, or 32 bytes). Given a 128-bit key, it has been estimated that the fastest computers on Earth using brute-force would need billions of years to crack Rijndael-encrypted data. And that's the smallest key size. 'nuff said.

    Here is a quick comparision of the performance of some of the ciphers in the Z Directory. The data was obtained by runnning Vettrasoft's krypt program on a Dell Inspiron laptop computer, with Intel Duo Core (T2050, 1.60 GHz; 2 GB RAM), running Microsoft Windows Server 2003 R2. For each cipher listed, the numbers are in megabytes per second; first for encryption, then decryption (separated by a semi-colon):

    file type & size Nile DES Rijndael
    4.5 MB binary file (non-exec) 0.35; 0.91 1.14; 1.01 0.57; 0.57 1.52; 1.52
    65 MB installer program (gnucash) 0.35; 0.85 0.83; 0.80 0.58; 0.57 1.06; 1.37

    usage.
    All the encryption objects here have the same basic usage, with a few minor differences. Unlike message transport, you need to select the cipher type in advance: chambran_crypt_o, blowfish_crypt_o, DES_crypt_o, pair_crypt_o, Rijndael_crypt_o, etc. They are all sub-classes of crypto_o. That class is not useful to the client application, except perhaps if you want to see which member functions need to exist in subtypes. The basic methodology is to create a cipher instance, set the password, then encrypt or decrypt a block of bytes. The block must be put into a properly-prepared string object. Typically, the string object is put into binary mode and the block size is prepped so that it matches the "chunk size" of the cipher. The example [below] contains a concise example of how to use the various cipher classes.

    note.
    The encryption components were formerly in their own group, "crypt". You may see references in the documentation to this name (not a gang). Vettrasoft is not in the encryption algorithms business, so for the most part, the source code was created elsewhere and has been modified to work in the Z Directory framework.

    Although some claim that encrypting a block of data already encrypted with another method doesn't buy you any more protection, you can readily do that with the Z Directory encryption components. Suppose you have 1,000 characters of text, and you encrypt it with DES. Worried about the proliferation of DES cracker hardware devices, you re-encrypt the DES-encrypted block with RSA. Just remember to decrypt it with RSA, then DES (reverse order) to get back to clear text. You just need to be careful with the block sizes, being careful when the output block size is different from the input block size, and making sure that the output block "input chunk size" is correct (eg chunk size of 16 indicates the input block length must be in multiples of 16. 256, 1600, 2400, and 11040 are valid block sizes. 15, 235, and 1590 are not).

    disclaimer: Vettrasoft wishes to acknowledge the use of various encryption methods not developed by Vettrasoft. Regarding any copyright issues: If Vettrasoft Corporation recieves any monetary compensation related to encryption algorithms contained within Vettrasoft products (eg, Z Directory archive libraries), such compensation is not for the code or algorithm itself (which is freely available in the public domain), but rather, for the construction of a software delivery system that provides a uniform interface to such code, including the work applied to (and not done by the original author) in making such software functionable in a clean, environment-independent framework such as the Z Directory by Vettrasoft.

    acknowledgements: thanks to:
    coolhash_crypt_o : the Regents of the University of California (and Rob McCool);
    chambran_crypt_o : Bill Chambers
    pair_crypt_o : Although the implementation of RSA here is external to the RSA Data Security, Inc., we thank them for their contribution to the evolution of RSA and any algorithms they created that made it into here.
    blowfish : Bruce Schneier, creator of the blowfish algorithm.
    twofish : Bruce Schneier (again).
    Rijndael : Raif S. Naffah and Paulo S.L.M. Barreto (and CodeProject.com) for making this great AES crypto available.

    A note about "methods" within a given encryption class: Some of the encryptors have "method types". These are simply different implementations of the given encryption methodology, invariably by different authors. Vettrasoft may more may not publish reports about the quality or peculiarities of these implementations. We will no doubt provide updates about the differences (or similarities) of different methods. When using a multi-method encryption object, you should probably stick to a specific one. There will always be a default in the case of multi-method encryptors.

    examples.
    The krypt program source code (see the sample downloads page) provides a wealth of information for using the cipher classes described here and the Z Directory in general. The following example is a fairly involved case of using 2 ciphers to encrypt some text.
    Some points:

    • When using multiple ciphers, you can use a pointer to the base class (crypto_o) for repititive operations.

    • A substantial amount of the work in this example goes to computing and managing block sizes. Buffer management in this example can be extended to the other Z Directory cipher objects.

    • In the case of non-stream ciphers, eg where the input and / or output bock size is not 1, or when the input / output size ratio is not 1, the size of the container (the string object) will probably need to be resized. In doing this, the original size of the data is lost. It is up to you to keep track of this. One possible solution is to encode the data (and possibly block size) into a header for the output (encrypted) block. This is actually done in Vettrasoft's krypt program (see download page). However, such additional functionality is not included in the layer 2 security-cryptography objects.

    • When using Rijndael or RSA, other [set-up type] functions are required.

    //-------------(BEGIN-SOURCE-CODE-BLOCK)--------------------------------
    
    #include "stdafx.h"
    #include "z_crypt.h"
    #include "z_file.h"
    #include "z_mathsubs.h"
    
    char *enemy_of_state =
    "the CIA and FBI, they don't want anybody to know about the plane crash.\n\
    And they will kill - by any way possible - anyone that wants to blow\n\
    the whistle on them, about the rendition flights that The Agency was using,\n\
    flights to and from Guantanamo. This secret information, commonly known\n\
    in Colombia, must not be revealed.";
    
    
    int main (int argc, char *argv[])
    {
        crypto_o *px = NULL;                       // make pointer, for loops
        nile_crypt_o     ciphero_nil;
        Rijndael_crypt_o ciphero_rij;
        string_o password ("abracadabra");
    
        int i, ie, ie2, factor = 1;
        size_t in_siz = 1, ot_siz = 1, orig_size;
        size_t nb_in, nb_out;
        string_o bytes_in  = enemy_of_state;        // str length is 323 bytes
        string_o orig_text = bytes_in;              // make a "backup" copy
        string_o bytes_ot;
    
        orig_size = bytes_in.size();                // "push" size, 4 later restore
        ciphero_rij.set_keysize   (32);
        ciphero_rij.set_blocksize (32);
        ciphero_rij.set_mode (zcrypto_mode_ECB);
    
        bytes_in.setmode_binary();                  // make so we can mold the
        bytes_in.setmode_sticky();                  // strings and not worry about
        bytes_ot.setmode_binary();                  // whether the sizes have been
        bytes_ot.setmode_sticky();                  // set to whatever we want
    
        for (i = 0; i < 2; i++)                     // loop twice, to compute max
        {                                           // input & output block sizes
            switch (i)
            {
            case 0: px = &ciphero_nil; break;
            case 1: px = &ciphero_rij; break;
            }
    
            px->set_key (password);                 // SAME PASSWORD, EACH CIPHER
    
            nb_in  = px->input_chunksize  (&ie);    // min block size of data input
            nb_out = px->output_chunksize (&ie);    // usually 1x, 2x of input size
            factor *= nb_out / nb_in;               // this just keeps growing
            in_siz = z_lcm (in_siz, nb_in);         // find lowest poss. multiple
            ot_siz = factor * in_siz;               // output size is mult of input
        }
    
        in_siz = z_ilum (bytes_in.size(), in_siz);  // get multiple of "in_siz"
        bytes_in.force_size (in_siz);               // expand size to 11 * 32 = 352
    
        ot_siz = factor * in_siz;                   // again, size is mult of input
        bytes_ot.resize (ot_siz);                   // 2 * 352 = 704
    
        //..........................................................
        // encrypt the data
        // round 1: output size goes to 2 * 352 = 704
        // round 2: output size unchanged (1:1; eg, 704 bytes)
        //..........................................................
        for (i = 0; i < 2; i++)
        {
            if (!i)
                px = &ciphero_nil;
            else
            {
                ciphero_rij.reset_chain();
                bytes_in = bytes_ot;                // transfer 1st-round encrypted
                px = &ciphero_rij;                  // to make it new the input
            }
    
            ie = px->encrypt (bytes_in, bytes_ot, &in_siz);
        }
    
        ot_siz = in_siz;
        string_o crypted = bytes_ot;
        string_o clear = "";
    
        //..........................................................
        // we'll write out the encrypted data block to a file
        //..........................................................
        file_o f_out;
        f_out.set_name ("hidden.txt");
        f_out.setmode_binary();
        ie = f_out.put_bytes (bytes_ot, &ot_siz, &ie2);
    
        //..........................................................
        // decrypt the data. note the reverse order of obj. pointer
        //..........................................................
        ciphero_rij.reset_chain();                  // MANDATORY!
    
        for (i = 0; i < 2; i++)
        {
            if (!i)
                px = &ciphero_rij;
            else                                    // transfer output -> input
                { px = &ciphero_nil; crypted = clear; }
    
            ie = px->decrypt (crypted, clear, &ot_siz);
        }
    
        clear.force_size (orig_size);               // "pop" the original size
        if (clear == orig_text)
            std::cout << "STRINGS MATCH.\n";
        else
            std::cout << "No MATCH.\n";
    
        return 0;
    }
    //-------------(END---SOURCE-CODE-BLOCK)--------------------------------
    

    history.

            ??? 12/28/1984: vettra1_crypt object created
            ??? 02/29/1996: bug fixed in DES
            Sun 11/17/1996: coolhash  bug fix, for 'binary' data
            Tue 11/18/1997: min() problems in vettra1 crypt
            ??? 11/06/1998: bug fixed in ROT-13
            Thu 11/28/1996: filecrypt_header_o initially created
            Sun 03/23/2003: filecrypt_header_o added to Z library
            Wed 04/06/2011: DE-ACTIVATED coolhash_crypt_o (doesn't work)
            Tue 04/12/2011: "simple hash" reborn - new impl. from Rob McCool; pair_crypt reinstated
            Fri 04/15/2011: Rijndael added to Z Directory
            Sat 01/14/2017: work on blowfish (impl. by Gaby Abed) started
            Wed 01/18/2017: twofish added to the Z Directory
            Fri 01/20/2017: 8:15am: twofish implementation working 100%
    

    limitations.
    iocrypt_o is intended to be used for processing many lines of input, and writing the encrypted (or decrypted, if going in reverse) output to files. With this object you can use multiple encryption algorithms chained together, each with its own unique password. Vettrasoft plans to add a multicrypt_o class, at a lower layer, which provides multiple chained encryptors (plus a uuencoding option) but doesn't have all the bells and whistles for reading and writing I/O to and from stdin or files.

    Having 2 separate classes would allow applications to use the simpler object to do a single encode-decode. The iocrypt_o class is somewhat cumbersome to use and oriented towards processing multiple lines of input.