[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7. Public Key cryptography (I)

Public key cryptography, also known as asymmetric cryptography, is an easy way for key management and to provide digital signatures. Libgcrypt provides two completely different interfaces to public key cryptography, this chapter explains the one based on S-expressions.

7.1 Available algorithms  Algorithms supported by the library.
7.2 Used S-expressions  Introduction into the used S-expression.
7.3 Public key modules  How to work with public key modules.
7.4 Cryptographic Functions  Functions for performing the cryptographic actions.
7.5 General public-key related Functions  General functions, not implementing any cryptography.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.1 Available algorithms

Libgcrypt supports the RSA (Rivest-Shamir-Adleman) algorithms as well as DSA (Digital Signature Algorithm) and ElGamal. The versatile interface allows to add more algorithms in the future.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.2 Used S-expressions

Libgcrypt's API for asymmetric cryptography is based on data structures called S-expressions (see XXXX) and does not work with contexts as most of the other building blocks of Libgcrypt do.

The following information are stored in S-expressions:

keys

plain text data

encrypted data

signatures

To describe how Libgcrypt expect keys, we use some examples. Note that words in uppercase indicate parameters whereas lowercase words are literals.

 
(private-key
  (dsa
    (p p-mpi)
    (q q-mpi)
    (g g-mpi)
    (y y-mpi)
    (x x-mpi)))

This specifies a DSA private key with the following parameters:

p-mpi
DSA prime .
q-mpi
DSA group order (which is a prime divisor of ).
g-mpi
DSA group generator .
y-mpi
DSA public key value .
x-mpi
DSA secret exponent x.

All the MPI values are expected to be in GCRYMPI_FMT_USG format. The public key is similar with "private-key" replaced by "public-key" and no x-mpi.

An easy way to create such an S-expressions is by using gcry_sexp_build which allows to pass a string with printf-like escapes to insert MPI values.

Here is an example for an RSA key:

 
(private-key
  (rsa
    (n n-mpi)
    (e e-mpi)
    (d d-mpi)
    (p p-mpi)
    (q q-mpi)
    (u u-mpi)

with

n-mpi
RSA public modulus .
e-mpi
RSA public exponent .
d-mpi
RSA secret exponent \bmod (p-1)(q-1)}.
p-mpi
RSA secret prime .
q-mpi
RSA secret prime with .
u-mpi
multiplicative inverse \bmod q}.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.3 Public key modules

Libgcrypt makes it possible to load additional `public key modules'; these public key algorithms can be used just like the algorithms that are built into the library directly. For an introduction into extension modules, see See section 3.2 Modules.

Data type: gcry_pk_spec_t
This is the `module specification structure' needed for registering public key modules, which has to be filled in by the user before it can be used to register a module. It contains the following members:

const char *name
The primary name of this algorithm.
char **aliases
A list of strings that are `aliases' for the algorithm. The list musdt be terminanted with a NULL element.
const char *elements_pkey
String containing the one-letter names of the MPI values contained in a public key.
const char *element_skey
String containing the one-letter names of the MPI values contained in a secret key.
const char *elements_enc
String containing the one-letter names of the MPI values that are the result of an encryption operation using this algorithm.
const char *elements_sig
String containing the one-letter names of the MPI values that are the result of a sign operation using this algorithm.
const char *elements_grip
String containing the one-letter names of the MPI values that are to be included in the `key grip'.
int use
The bitwise-OR of the following flags, depending on the abilities of the algortihm:
GCRY_PK_USAGE_SIGN
The algorithm supports signing and verifying of data.
GCRY_PK_USAGE_ENCR
The algorithm supports the encryption and decryption of data.
gcry_pk_generate_t generate
The function responsible for generating a new key pair. See below for a description of this type.
gcry_pk_check_secret_key_t check_secret_key
The function responsible for checking the sanity of a provided secret key. See below for a description of this type.
gcry_pk_encrypt_t encrypt
The function responsible for encrypting data. See below for a description of this type.
gcry_pk_decrypt_t decrypt
The function responsible for decrypting data. See below for a description of this type.
gcry_pk_sign_t sign
The function reponsible for signing data. See below for a description of this type.
gcry_pk_verify_t verify
The function responsible for verifying that the provided signature matches the provided data. See below for a description of this type.
gcry_pk_get_nbits_t get_nbits
The function reponsible for returning the number of bits of a provided key. See below for a description of this type.

Data type: gcry_pk_generate_t
Type for the `generate' function, defined as: gcry_err_code_t (*gcry_pk_generate_t) (int algo, unsigned int nbits, unsigned long use_e, gcry_mpi_t *skey, gcry_mpi_t **retfactors)

Data type: gcry_pk_check_secret_key_t
Type for the `check_secret_key' function, defined as: gcry_err_code_t (*gcry_pk_check_secret_key_t) (int algo, gcry_mpi_t *skey)

Data type: gcry_pk_encrypt_t
Type for the `encrypt' function, defined as: gcry_err_code_t (*gcry_pk_encrypt_t) (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *pkey, int flags)

Data type: gcry_pk_decrypt_t
Type for the `decrypt' function, defined as: gcry_err_code_t (*gcry_pk_decrypt_t) (int algo, gcry_mpi_t *result, gcry_mpi_t *data, gcry_mpi_t *skey, int flags)

Data type: gcry_pk_sign_t
Type for the `sign' function, defined as: gcry_err_code_t (*gcry_pk_sign_t) (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *skey)

Data type: gcry_pk_verify_t
Type for the `verify' function, defined as: gcry_err_code_t (*gcry_pk_verify_t) (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey, int (*cmp) (void *, gcry_mpi_t), void *opaquev)

Data type: gcry_pk_get_nbits_t
Type for the `get_nbits' function, defined as: unsigned (*gcry_pk_get_nbits_t) (int algo, gcry_mpi_t *pkey)

Function: gcry_error_t gcry_pk_register (gcry_pk_spec_t *pubkey, unsigned int *algorithm_id, gcry_module_t *module)

Register a new public key module whose specification can be found in pubkey. On success, a new algorithm ID is stored in algorithm_id and a pointer representhing this module is stored in module.

Function: void gcry_pk_unregister (gcry_module_t module)
Unregister the public key module identified by module, which must have been registered with gcry_pk_register.

Function: gcry_error_t gcry_pk_list (int *list, int *list_length)
Get a list consisting of the IDs of the loaded pubkey modules. If list is zero, write the number of loaded pubkey modules to list_length and return. If list is non-zero, the first *list_length algorithm IDs are stored in list, which must be of according size. In case there are less pubkey modules than *list_length, *list_length is updated to the correct number.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.4 Cryptographic Functions

Note, that we will in future allow to use keys without p,q and u specified and may also support other parameters for performance reasons.

Some functions operating on S-expressions support `flags', that influence the operation. These flags have to be listed in a sub-S-expression named `flags'; the following flags are known:

pkcs1
Use PKCS#1 block type 2 padding.
no-blinding
Do not use a technique called `blinding', which is used by default in order to prevent leaking of secret information. Blinding is only implemented by RSA, but it might be implemented by other algorithms in the future as well, when necessary.

Now that we know the key basics, we can carry on and explain how to encrypt and decrypt data. In almost all cases the data is a random session key which is in turn used for the actual encryption of the real data. There are 2 functions to do this:

Function: gcry_error_t gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t data, gcry_sexp_t pkey)

Obviously a public key must be provided for encryption. It is expected as an appropriate S-expression (see above) in pkey. The data to be encrypted can either be in the simple old format, which is a very simple S-expression consisting only of one MPI, or it may be a more complex S-expression which also allows to specify flags for operation, like e.g. padding rules.

If you don't want to let Libgcrypt handle the padding, you must pass an appropriate MPI using this expression for data:

 
(data
  (flags raw)
  (value mpi))

This has the same semantics as the old style MPI only way. MPI is the actual data, already padded appropriate for your protocol. Most systems however use PKCS#1 padding and so you can use this S-expression for data:

 
(data
  (flags pkcs1)
  (value block))

Here, the "flags" list has the "pkcs1" flag which let the function know that it should provide PKCS#1 block type 2 padding. The actual data to be encrypted is passed as a string of octets in block. The function checks that this data actually can be used with the given key, does the padding and encrypts it.

If the function could successfully perform the encryption, the return value will be 0 and a a new S-expression with the encrypted result is allocated and assign to the variable at the address of r_ciph. The caller is responsible to release this value using gcry_sexp_release. In case of an error, an error code is returned and r_ciph will be set to NULL.

The returned S-expression has this format when used with RSA:

 
(enc-val
  (rsa
    (a a-mpi)))

Where a-mpi is an MPI with the result of the RSA operation. When using the ElGamal algorithm, the return value will have this format:

 
(enc-val
  (elg
    (a a-mpi)
    (b b-mpi)))

Where a-mpi and b-mpi are MPIs with the result of the ElGamal encryption operation.

Function: gcry_error_t gcry_pk_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t data, gcry_sexp_t skey)

Obviously a private key must be provided for decryption. It is expected as an appropriate S-expression (see above) in skey. The data to be decrypted must match the format of the result as returned by gcry_pk_encrypt, but should be enlarged with a flags element:

 
(enc-val
  (flags)
  (elg
    (a a-mpi)
    (b b-mpi)))

Note, that this function currently does not know of any padding methods and the caller must do any un-padding on his own.

The function returns 0 on success or an error code. The variable at the address of r_plain will be set to NULL on error or receive the decrypted value on success. The format of r_plain is a simple S-expression part (i.e. not a valid one) with just one MPI if there was no flags element in data; if at least an empty flags is passed in data, the format is:

 
(value plaintext)

Another operation commonly performed using public key cryptography is signing data. In some sense this is even more important than encryption because digital signatures are an important instrument for key management. Libgcrypt supports digital signatures using 2 functions, similar to the encryption functions:

Function: gcry_error_t gcry_pk_sign (gcry_sexp_t *r_sig, gcry_sexp_t data, gcry_sexp_t skey)

This function creates a digital signature for data using the private key skey and place it into the variable at the address of r_sig. data may either be the simple old style S-expression with just one MPI or a modern and more versatile S-expression which allows to let Libgcrypt handle padding:

 
 (data
  (flags pkcs1)
  (hash hash-algo block))

This example requests to sign the data in block after applying PKCS#1 block type 1 style padding. hash-algo is a string with the hash algorithm to be encoded into the signature, this may be any hash algorithm name as supported by Libgcrypt. Most likely, this will be "sha1", "rmd160" or "md5". It is obvious that the length of block must match the size of that message digests; the function checks that this and other constraints are valid.

If PKCS#1 padding is not required (because the caller does already provide a padded value), either the old format or better the following format should be used:

 
(data
  (flags raw)
  (value mpi))

Here, the data to be signed is directly given as an MPI.

The signature is returned as a newly allocated S-expression in r_sig using this format for RSA:

 
(sig-val
  (rsa
    (s s-mpi)))

Where s-mpi is the result of the RSA sign operation. For DSA the S-expression returned is:

 
(sig-val
  (dsa
    (r r-mpi)
    (s s-mpi)))

Where r-mpi and s-mpi are the result of the DSA sign operation. For ElGamal signing (which is slow, yields large numbers and probably is not as secure as the other algorithms), the same format is used with "elg" replacing "dsa".

The operation most commonly used is definitely the verification of a signature. Libgcrypt provides this function:

Function: gcry_error_t gcry_pk_verify (gcry_sexp_t sig, gcry_sexp_t data, gcry_sexp_t pkey)

This is used to check whether the signature sig matches the data. The public key pkey must be provided to perform this verification. This function is similar in its parameters to gcry_pk_sign with the exceptions that the public key is used instead of the private key and that no signature is created but a signature, in a format as created by gcry_pk_sign, is passed to the function in sig.

The result is 0 for success (i.e. the data matches the signature), or an error code where the most relevant code is GCRYERR_BAD_SIGNATURE to indicate that the signature does not match the provided data.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

7.5 General public-key related Functions

A couple of utility functions are available to retrieve the length of the key, map algorithm identifiers and perform sanity checks:

Function: const char * gcry_pk_algo_name (int algo)

Map the public key algorithm id algo to a string representation of the algorithm name. For unknown algorithms this functions returns an empty string.

Function: int gcry_pk_map_name (const char *name)

Map the algorithm name to a public key algorithm Id. Returns 0 if the algorithm name is not known.

Function: int gcry_pk_test_algo (int algo)

Return 0 if the public key algorithm algo is available for use. Note, that this is implemented as a macro.

Function: unsigned int gcry_pk_get_nbits (gcry_sexp_t key)

Return what is commonly referred as the key length for the given public or private in key.

Function: unsigned char * gcry_pk_get_keygrip (gcry_sexp_t key, unsigned char *array)

Return the so called "keygrip" which is the SHA-1 hash of the public key parameters expressed in a way depended on the algorithm. array must either provide space for 20 bytes or NULL;. In the latter case a newly allocated array of that size is returned. On success a pointer to the newly allocated space or to array is returned. NULL is returned to indicate an error which is most likely an unknown algorithm or one where a "keygrip" has not yet been defined. The function accepts public or secret keys in key.

Function: gcry_error_t gcry_pk_testkey (gcry_sexp_t key)

Return zero if the private key key is `sane', an error code otherwise. Note, that it is not possible to chek the `saneness' of a public key.

Function: int gcry_pk_algo_info (int algo, int what, void *buffer, size_t *nbytes)

Depending on the value of what return various information about the public key algorithm with the id algo. Note, that the function returns -1 on error and the actual error code must be retrieved using the function gcry_errno. The currently defined values for what are:

GCRYCTL_TEST_ALGO:
Return 0 when the specified algorithm is available for use. buffer must be NULL, nbytes may be passed as NULL or point to a variable with the required usage of the algorithm. This may be 0 for "don't care" or the bit-wise OR of these flags:

GCRY_PK_USAGE_SIGN
Algorithm is usable for signing.
GCRY_PK_USAGE_ENCR
Algorithm is usable for encryption.

GCRYCTL_GET_ALGO_USAGE:
Return the usage flags for the given algorithm. An invalid algorithm return 0. Disabled algorithms are ignored here because we want to know whether the algorithm is at all capable of a certain usage.

GCRYCTL_GET_ALGO_NPKEY
Return the number of elements the public key for algorithm algo consist of. Return 0 for an unknown algorithm.

GCRYCTL_GET_ALGO_NSKEY
Return the number of elements the private key for algorithm algo consist of. Note that this value is always larger than that of the public key. Return 0 for an unknown algorithm.

GCRYCTL_GET_ALGO_NSIGN
Return the number of elements a signature created with the algorithm algo consists of. Return 0 for an unknown algorithm or for an algorithm not capable of creating signatures.

GCRYCTL_GET_ALGO_NENC
Return the number of elements a encrypted message created with the algorithm algo consists of. Return 0 for an unknown algorithm or for an algorithm not capable of encryption.

Please note that parameters not required should be passed as NULL.

Function: gcry_error_t gcry_pk_ctl (int cmd, void *buffer, size_t buflen)

This is a general purpose function to perform certain control operations. cmd controls what is to be done. The return value is 0 for success or an error code. Currently supported values for cmd are:

GCRYCTL_DISABLE_ALGO
Disable the algorithm given as an algorithm id in buffer. buffer must point to an int variable with the algorithm id and buflen must have the value sizeof (int).

Libgcrypt also provides a function for generating public key pairs:

Function: gcry_error_t gcry_pk_genkey (gcry_sexp_t *r_key, gcry_sexp_t parms)

This function create a new public key pair using information given in the S-expression parms and stores the private and the public key in one new S-expression at the address given by r_key. In case of an error, r_key is set to NULL. The return code is 0 for success or an error code otherwise.

Here is an example for parms for creating a 1024 bit RSA key:

 
(genkey
  (rsa
    (nbits 4:1024)))

To create an ElGamal key, substitute "elg" for "rsa" and to create a DSA key use "dsa". Valid ranges for the key length depend on the algorithms; all commonly used key lengths are supported. Currently supported parameters are:

nbits
This is always required to specify the length of the key. The argument is a string with a number in C-notation.

rsa-use-e
This is only used with RSA to give a hint for the public exponent. The value will be used as a base to test for a usable exponent. Some values are special:

`0'
Use a secure and fast value. This is currently the number 41.
`1'
Use a secure value as required by some specification. This is currently the number 65537.
`2'
Reserved

If this parameter is not used, Libgcrypt uses for historic reasons 65537.

The key pair is returned in a format depending on the algorithm. Both private and public keys are returned in one container and may be accompanied by some miscellaneous information.

As an example, here is what the ElGamal key generation returns:

 
(key-data
  (public-key
    (elg
      (p p-mpi)
      (g g-mpi)
      (y y-mpi)))
  (private-key
    (elg
      (p p-mpi)
      (g g-mpi)
      (y y-mpi)
      (x x-mpi)))
  (misc-key-info
    (pm1-factors n1 n2 ... nn)))

As you can see, some of the information is duplicated, but this provides an easy way to extract either the public or the private key. Note that the order of the elements is not defined, e.g. the private key may be stored before the public key. n1 n2 ... nn is a list of prime numbers used to composite p-mpi; this is in general not a very useful information.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by root on April, 16 2004 using texi2html