Varnish Module (VMOD) providing access to the libgcrypt library of cryptographic building blocks -- the same library used by the GNU Privacy Guard cryptographic suite (GnuPG or GPG)
Find a file
2025-09-15 16:33:25 +02:00
src Adjust to changed error reporting 2025-09-15 16:33:25 +02:00
.dir-locals.el Initial commit -- passes 00basic.vtc, but other code is not yet tested. 2017-04-07 18:36:36 +02:00
.gitignore Handle src/vmod_vcs_version.txt 2025-05-12 10:55:12 +02:00
bootstrap Migrate to VCDK 2025-05-12 10:55:12 +02:00
configure.ac Migrate to VCDK 2025-05-12 10:55:12 +02:00
CONTRIBUTING.rst Initial commit -- passes 00basic.vtc, but other code is not yet tested. 2017-04-07 18:36:36 +02:00
INSTALL.rst Migrate to VCDK 2025-05-12 10:55:12 +02:00
LICENSE Standardize LICENSE 2022-12-01 16:23:26 +01:00
Makefile.am Migrate to VCDK 2025-05-12 10:55:12 +02:00
README.rst Update generated docs 2023-06-23 14:38:55 +02:00

..
.. NB:  This file is machine generated, DO NOT EDIT!
..
.. Edit ../src/vmod_gcrypt.vcc and run make instead
..

.. role:: ref(emphasis)

===========
vmod_gcrypt
===========

------------------------------------------
access the libgcrypt cryptographic library
------------------------------------------

:Manual section: 3

SYNOPSIS
========

.. parsed-literal::

  import gcrypt [as name] [from "path"]
  
  VOID init(ENUM, BYTES n)
  
  new xsymmetric = gcrypt.symmetric(ENUM cipher, ENUM mode, ENUM padding, BLOB key, BOOL secure, BOOL cbc_cts)
  
      BLOB xsymmetric.encrypt(BLOB plaintext, [BLOB iv], [BLOB ctr])
   
      BLOB xsymmetric.decrypt(BLOB ciphertext, [BLOB iv], [BLOB ctr])
   
  BLOB fileread(STRING path)
  
  BLOB random([ENUM quality], BYTES n)
  
  INT random_int(ENUM quality, INT bound)
  
  REAL random_real(ENUM)
  
  BOOL random_bool(ENUM)
  
  VOID wipe(BLOB)
  
  STRING version()
  
  STRING gcrypt_version()
  
::

  gcrypt.init(ENUM {INIT_SECMEM, DISABLE_SECMEM} [, BYTES n])
  gcrypt.init(FINISH)

  new OBJECT = gcrypt.symmetric(ENUM cipher, ENUM mode, ENUM padding,
                                BLOB key [, BOOL secure]
				[, BOOL cbc_cts])
  BLOB <OBJ>.encrypt(BLOB plaintext [, BLOB iv] [, BLOB ctr])
  BLOB <OBJ>.decrypt(BLOB ciphertext [, BLOB iv] [, BLOB ctr])

  BLOB gcrypt.fileread(STRING path)

  BLOB gcrypt.random([ENUM quality] [, BYTES n])
  INT gcrypt.random_int(ENUM quality [, INT bound])
  REAL gcrypt.random_real(ENUM)
  BOOL random_bool(ENUM)

  gcrypt.wipe(BLOB)

  STRING gcrypt.version()
  STRING gcrypt.gcrypt_version()

DESCRIPTION
===========

This Varnish Module (VMOD) provides access to the libgcrypt library of
cryptographic building blocks -- the same library used by the GNU
Privacy Guard cryptographic suite (GnuPG or GPG). The VMOD currently
supports:

* symmetric encryption with AES, and with the standard modes of
  operation

* generation of pseudo-random data

The VMOD uses the VCL data type BLOB for data that enter into
encryption operations -- plaintext, ciphertext, initialization vectors
and counter vectors. BLOBs are arbitrary regions of memory; this
allows the VMOD to concentrate strictly on cryptographic operations,
separate from concerns of binary-to-text encodings. BLOBs are not
created by any part of native VCL, and can only be created by other
VMODs, so it is necessary to use this VMOD together with another one
that does so (such as VMOD ``blob`` for binary-to-text encodings,
which is part of varnish-cache).

This is a simple usage example::

  import gcrypt;
  import blob;

  sub vcl_init {
      # Finalize default initialization of the libgcrypt library.
      # This must be called once during the lifetime of the Varnish
      # child process, as explained below.
      gcrypt.init(FINISH);

      # Create an object for AES-128 in CTR mode using the key just
      # created, and with libgcrypt internal structures in secure memory.
      # The key is read from the given file.
      new aes = gcrypt.symmetric(AES, CTR, secure=true,
                                 key=gcrypt.fileread("/path/to/key"));
  }

  # Assume that a plaintext to be encrypted is in the response
  # header X-Msg. Assign the hex-encoded encrypted message to the
  # response header X-Cipher, and the hex-encoded counter vector to
  # the response header X-Ctr; and remove X-Msg from the response.
  sub vcl_deliver {
      # Use the blob VMOD to convert the contents of X-Msg to a
      # BLOB, and to encode the encrypted ciphertext in hex with
      # lower-case digits. Use the random() function to generate a
      # counter vector as a 128 bit nonce.
      set resp.http.X-Cipher
          = blob.encode(HEXLC,
              aes.encrypt(blob.decode(encoded=req.http.X-Msg),
                          ctr=gcrypt.random(NONCE, 16B)));

      # Use the no-argument version of random() to retrieve the
      # counter vector that was just generated, and use the
      # blob VMOD to encode it as lower-case hex.
      set resp.http.X-CTR = blob.encode(HEXLC, gcrypt.random());

      # Remove the plaintext from the response header.
      unset resp.http.X-Msg;
  }

libgcrypt secure memory
-----------------------

libgcrypt supports a notion of "secure memory" for storing sensitive
data, which is used by the library internally when enabled. What this
entails is platform-dependent, but on most platforms supported by
Varnish:

* The secure memory pool is locked (with mlock(2)), so that it will
  not be paged to swap space.

* The memory pool is allocated in pages that cannot be read by other
  processes.

* Memory obtained from the pool is overwritten after use.

* On some platforms, privileges/capabilities of the running process
  are lowered when secure memory is initialized. This applies to the
  varnishd child process.

The use of secure memory is turned off by default for the creation of
new objects. With the VMOD, you can specify the use of secure memory
by setting the ``secure`` flag to ``true`` in a constructor for
symmetric encryption, and configure the size of the pool, or disable
secure memory, using the ``init()`` function, as described below.

libgcrypt logging
-----------------

The libgcrypt library emits log messages that the VMOD directs to the
Varnish shared memory log. These messages are logged with the pseudo
XID 0, which is only visible when ``varnishlog(1)`` is used with raw
grouping (command-line option ``-g raw``) -- they do not appear with
default grouping. libgcrypt log levels ERROR, FATAL and BUG appear in
the Varnish log with the tag ``Error``; all other libgcrypt log
messages appear with the tag ``Debug``.

libgcrypt fatal errors
----------------------

The libgcrypt library has a fatal error handler that causes the
process to abort, and hence stops the Varnish child process. The VMOD
endeavors to prevent this from happening (with error handling as
described below), but if it does:

* The error message and numeric error code are written to the Varnish
  log with XID 0 and the ``Error`` tag.

* A Varnish panic is invoked with the error message from libgcrypt.

.. _gcrypt.init():

VOID init(ENUM, BYTES n)
------------------------

::

   VOID init(ENUM {INIT_SECMEM, DISABLE_SECMEM, FINISH}, BYTES n=1)

Initialize the libgcrypt library, currently to manage the use of
secure memory. The ENUM specifies an operation for initialization.

Restricted to: ``vcl_init``.

Initialization takes place *exactly once* during the lifetime of the
Varnish child process, and must be performed before any objects are
created; details below.

With ``INIT_SECMEM``, you can configure the size of the secure memory
pool to ``n`` bytes (the ``n`` parameter is ignored for the other
ENUMs). The data type for ``n`` is BYTES, so the value must be written
with a suffix such as B or KB. Secure memory is enabled by default
and, if configured, a minimum size is enforced by libgcrypt (32 KiB in
libgcrypt 1.6.3), so you don't have to call ``init()`` with
``INIT_SECMEM`` to use the default.

The most likely cause for ``INIT_SECMEM`` to fail with ``Cannot
initialize secure memory ... gcrypt/General error`` is that locking
the memory failed. In this case ensure that the resource limits are
configured appropriately (check ``ulimit -l``).

Setting ``n`` to 0B with ``INIT_SECMEM`` disables secure memory, and
hence has the same effect as calling ``init(DISABLE_SECMEM)``. If
secure memory is enabled, libgcrypt imposes a minimum size for the
pool (16 KiB for libgcrypt 1.6.3), so any value of ``n`` that is
smaller than the minimum will result in the minimum allocation. Since
the default value of ``n`` is 1B, you can specify the minimum size by
calling ``init(INIT_SECMEM)`` without the ``n`` parameter.

``DISABLE_SECMEM`` disables secure memory; when secure memory is
disabled (either with ``init(DISABLE_SECMEM)`` or
``init(INIT_SECMEM, 0B)``), any attempt to create an object with the
``secure`` flag results in failure. If secure memory is enabled and
then disabled before ``init()`` is called with ``FINISH``, or vice
versa, the last setting holds.

``FINISH`` indicates that initialization is finalized, and must be
called before any of the VMOD's objects can be created. The simplest
initialization is simply to call ``init(FINISH)``, which results in
the default configuration for the libgcrypt library -- secure memory
is enabled with the default pool size.

Initialization is evaluated only once during the lifetime of the
Varnish child process. If a new instance of VCL is loaded with calls
to ``init()`` in ``vcl_init`` after initialization was already
performed for another instance, then an error message is logged (with
XID 0 and the ``Debug`` tag), and the calls are ignored (but the VCL
reload does not fail). This is true even if the prior VCL instance is
unloaded with the CLI command ``vcl.discard``. This means in
particular that the configuration of secure memory cannot be changed
at runtime; for that, you have to restart the Varnish child process,
with a new instance of VCL that changes the configuration.

If initialization has been finalized in a previously loaded VCL
instance, then a new VCL instance can be loaded that does not include
any ``init()`` calls and uses the VMOD; in that case, the prior
initialization continues to hold. But the simplest strategy is
probably to test a configuration that works for your deployment in the
long run, and invoke that configuration in every VCL instance that you
load at runtime. After the first load, subsequent invocations of
``init()`` are ignored, but are harmless.

Examples::

  import gcrypt;

  sub vcl_init {
      # Default initialization -- secure memory is enabled with
      # the default size for the pool.
      gcrypt.init(FINISH);
  }

  # Note that changed initializations are only effective after a
  # restart of the Varnish child process, as explained above.

  sub vcl_init {
      # Enable secure memory and allocate a 64KiB pool.
      gcrypt.init(INIT_SECMEM, 64KB);
      gcrypt.init(FINISH);
  }

  sub vcl_init {
      # Disable secure memory.
      gcrypt.init(DISABLE_SECMEM);
      gcrypt.init(FINISH);
  }

.. _gcrypt.symmetric():

new xsymmetric = gcrypt.symmetric(ENUM cipher, ENUM mode, ENUM padding, BLOB key, BOOL secure, BOOL cbc_cts)
------------------------------------------------------------------------------------------------------------

::

   new xsymmetric = gcrypt.symmetric(
      ENUM {AES, AES128, RIJNDAEL, RIJNDAEL128, AES192, RIJNDAEL192, AES256, RIJNDAEL256} cipher,
      ENUM {ECB, CFB, CBC, OFB, CTR} mode,
      ENUM {PKCS7, ISO7816, X923, NONE} padding=PKCS7,
      BLOB key,
      BOOL secure=0,
      BOOL cbc_cts=0
   )

Create an object for encryption and decryption with symmetric ciphers.
Currently, only AES is supported, with key lengths 128, 192 and 256.

The ``cipher`` ENUM specifies the cryptographic algorithm to be used:

* ``AES``, ``AES128``, ``RIJNDAEL``, ``RIJNDAEL128``: AES-128 (the ENUMs
  are aliases for one another)

* ``AES192``, ``RIJNDAEL192``: AES-192

* ``AES256``, ``RIJNDAEL256``: AES-256

The ``mode`` ENUM specifies the mode of operation.

The ``padding`` ENUM specifies the padding method, for modes of
operation that require it. Padding is required for ``ECB`` and
``CBC``; except that padding is not required for ``CBC`` with
ciphertext stealing, when the ``cbc_cts`` flag is set to ``true``. If
the mode of operation does not require padding, then the value of
``padding`` is ignored.

* ``PKCS7``: PKCS#7 padding (sometimes called PKCS#5). A padding byte
  is always added to the message, and the values of all padding bytes
  are the number of padding bytes added. This is the default value of
  ``padding``.

* ``ISO7816``: ISO/IEC 7816-4:2005 padding. Byte 0x80 is always added
  to the message, and the remaining padding bytes are set to 0x00.

* ``X923``: ANSI X.923 padding. A padding byte is always added to the
  message, and the value of the last byte is the number of padding
  bytes added. The remaining padding bytes are set to 0x00.

* ``NONE``: no padding. This can be used if you are certain that the
  length of all messages to be encrypted or decrypted will always be
  an exact multiple of the cipher's block length, and the other party
  also uses no padding.

The ``key`` BLOB is the cryptographic key to be used. Its size MUST
be correct for the cipher that is to be used; for example, 16 bytes
for AES-128.

If the ``secure`` flag is true, then secure memory is used by the
internal libgcrypt structures created for the object. False by default.

If the ``cbc_cts`` flag is true and ``CBC`` mode is specified, then
CBC is used with ciphertext stealing (and padding is not required).
``cbc_cts`` is ignored for all other modes of operation. Default is
``false``.

The creation of a ``symmetric`` object fails, leading to a failure
of the VCL load with an error message, under these conditions:

* libgcrypt initialization has not been finalized (``init(FINISH)``
  has never been called).

* ``key`` is NULL, for example due to failure to create a BLOB
  object.

* The size of the ``key`` BLOB is larger than the longest key length
  supported for ``cipher``.

* The ``secure`` flag is ``true``, but secure memory has been
  disabled.

* Any failure of the libgcrypt library to initialize internal
  structures to be used for this object. This happens, for example, if
  the key length is to short for the chosen cipher.

Examples::

  import gcrypt;
  import blob;

  # Assume in the following that initialization has been finalized.

  sub vcl_init {
      # Use the blob VMOD to create some BLOBs for the cryptographic
      # keys, whose lengths are 16, 24 and 32 bytes for AES-128, -192
      # and -256, respectively.
      # NOTE: The keys used in this manual's examples are chosen to
      # make the key lengths easy to recognize. DO NOT copy them
      # into production VCL!
      new k128 = blob.blob(HEX, "000102030405060708090a0b0c0d0e0f");
      new k192 = blob.blob(HEX,
                    "000102030405060708090a0b0c0d0e0f1011121314151617");
      new k256 = blob.blob(HEX,
    "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");

      # Create an object for AES-128 with CTR mode (no padding
      # required), not using secure memory by default.
      new aes128 = gcrypt.symmetric(AES128, CTR, key=k128.get());

      # Create an object for AES-192 with CBC mode and PKCS#7
      # padding, using secure memory.
      new aes192 = gcrypt.symmetric(AES192, CBC, PKCS7, key=k192.get(),
                                    secure=true);

      # Create an object for AES-256 with CBC mode and ciphertext
      # stealing (no padding required), using secure memory.
      new aes256 = gcrypt.symmetric(AES256, CBC, key=k256.get(),
                                    cbc_cts=true, secure=true);
  }

.. _xsymmetric.encrypt():

BLOB xsymmetric.encrypt(BLOB plaintext, [BLOB iv], [BLOB ctr])
--------------------------------------------------------------

::

      BLOB xsymmetric.encrypt(BLOB plaintext, [BLOB iv], [BLOB ctr])

Encrypt the contents of ``plaintext`` according to the parameters
specified for this ``symmetric`` object, and return the ciphertext as
a BLOB. The BLOB ``plaintext`` MAY NOT be NULL.

The ``iv`` and ``ctr`` parameters specify initialization and counter
vectors, respectively, and MUST be non-NULL for modes of operation
that require them. They MUST also be of the size required for the
cipher in use for the ``symmetric`` object. An initialization vector
is required for CFB, CBC and OFB modes, and a counter vector is
required for CTR. ``iv`` and ``ctr`` are ignored for modes of
operation that do not require them, and can be left out in those
cases.

The method fails under the following conditions:

* There is insufficient workspace for the BLOB returned as the
  ciphertext, or for the padded plaintext, if padding is necessary.

* The ``iv`` or ``ctr`` parameters are NULL for modes of operation
  that require init or counter vectors.

* Any failure of the libgcrypt library -- setting the init or counter
  vectors, or encrypting the message.

If the method fails, a message is logged using the ``VCL_Error`` tag,
and NULL is returned.

Examples::

  # In the following assume:
  # - The aes128, aes192 and aes256 objects from the previous examples.
  # - The messages to be encrypted are in the response headers
  #   X-Msg-128, X-Msg-192 and X-Msg-256.
  # - An appropriately sized base64-encoded counter vector is in
  #   the response header X-Ctr.
  # - Appropriately sized base64-encoded init vectors are in the
  #   response headers X-IV-192 and X-IV-256.
  sub vcl_deliver {
      # Encrypt X-Msg-128 with AES-128 and the counter vector, and
      # return the ciphertext in a base64-encoded response header.
      set resp.http.X-Cipher-128 = blob.encode(BASE64,
          aes128.encrypt(blob.decode(encoded=resp.http.X-Msg-128),
                         ctr=blob.decode(BASE64, resp.http.X-Ctr)));

      # Encrypt X-Msg-192 with AES-192 and init vector X-IV-192.
      set resp.http.X-Cipher-192 = blob.encode(BASE64,
          aes192.encrypt(blob.decode(encoded=resp.http.X-Msg-192),
                         iv=blob.decode(BASE64, resp.http.X-IV-192)));

      # Encrypt X-Msg-256 with AES-256 and init vector X-IV-256.
      set resp.http.X-Cipher-256 = blob.encode(BASE64,
          aes256.encrypt(blob.decode(encoded=resp.http.X-Msg-256),
                         iv=blob.decode(BASE64, resp.http.X-IV-256)));
  }

.. _xsymmetric.decrypt():

BLOB xsymmetric.decrypt(BLOB ciphertext, [BLOB iv], [BLOB ctr])
---------------------------------------------------------------

::

      BLOB xsymmetric.decrypt(BLOB ciphertext, [BLOB iv], [BLOB ctr])

Decrypt the contents of ``ciphertext`` according to the parameters
specified for this ``symmetric`` object, and return the plaintext as
a BLOB. The BLOB ``ciphertext`` MAY NOT be NULL.

As with the ``.encrypt()`` method, the ``iv`` and ``ctr`` parameters
specify initialization and counter vectors, and MUST be specified for
modes of operation that require them, but may be left out otherwise.

The failure conditions of ``.decrypt()`` are similar to those for
``.encrypt()``:

* Insufficient workspace for the ciphertext to be returned.

* Missing ``iv`` or ``ctr`` parameters for modes of operation that
  require them.

* An error in the padding bytes, if padding is specified. For example,
  if the number of padding bytes added is longer than the cipher's
  block length (for ``PKCS7`` and ``X923`` padding), or if a value
  such as 0x80 or 0x00 is not found where the padding scheme requires
  it.

* Failure of the libgcrypt library.

If the method fails, a message is logged using the ``VCL_Error`` tag,
and NULL is returned.

Examples::

  # Assume the aes128, aes192 and aes256 objects from the previous
  # examples. Also assume the messages to be decrypted are in the
  # headers X-Msg-128, X-Msg-192 and X-Msg-256, as well as the headers
  # X-Ctr, X-IV-192 and X-IV-256 as described for the .encrypt()
  # example above, except that they are all request headers.
  sub vcl_recv {
      # Decrypt X-Msg-128 with AES-128 and the counter vector, and
      # return the plaintext in a base64-encoded request header.
      set req.http.X-Cipher-128 = blob.encode(BASE64,
          aes128.decrypt(blob.decode(encoded=req.http.X-Msg-128),
                         ctr=blob.decode(BASE64, req.http.X-Ctr)));

      # Decrypt X-Msg-192 with AES-192 and init vector X-IV-192.
      set req.http.X-Cipher-192 = blob.encode(BASE64,
          aes192.decrypt(blob.decode(encoded=req.http.X-Msg-192),
                         iv=blob.decode(BASE64, req.http.X-IV-192)));

      # Decrypt X-Msg-256 with AES-256 and init vector X-IV-256.
      set req.http.X-Cipher-256 = blob.encode(BASE64,
          aes256.decrypt(blob.decode(encoded=req.http.X-Msg-256),
                         iv=blob.decode(BASE64, req.http.X-IV-256)));
  }

.. _gcrypt.fileread():

BLOB fileread(STRING path)
--------------------------

Return the contents of the file at ``path`` in a BLOB. This function
is provided for the specific purpose of reading sensitive data that
are needed during ``vcl_init``, such as cryptographic keys, so that
they are not exposed in the VCL source.

If secure memory is enabled, then space for the BLOB contents is
allocated from the secure memory pool; otherwise, space is allocated
from the heap. The contents are retained in memory for the duration
of the current task scope; this means:

* If ``fileread()`` is called in ``vcl_init`` (or ``vcl_fini``), then
  the scope ends when the execution of that subroutine ends.

* Otherwise (in any other VCL subroutine), the scope ends when the
  current client or backend transaction ends.

For example, if ``fileread()`` is called during one of the
``vcl_backend_*`` subroutines, then the scope ends when the current
backend transaction is complete.

When the task scope ends, then the BLOB contents are overwritten in
memory, and the allocated memory is freed. If the contents were
allocated from the secure memory pool, then that space is returned to
the pool.

``fileread()`` fails and returns NULL if:

* libgcrypt initialization is not finished

* ``path`` is NULL

* ``path`` does not denote a regular file. For example, ``path`` MAY
  NOT be a directory, symbolic link or named pipe.

* ``path`` cannot be inspected by stat(2), opened, read or closed,
  for example if the file does not exist, or if the Varnish child
  process has insufficient privileges to read it.

* ``path`` is changed at some time between just after invocation of
  ``fileread()`` and just after reading its contents.

* There is insufficient space for the necessary allocations, for
  example if the secure memory pool is to be used but does not have
  enough free memory.

If ``fileread()`` fails, it writes an error message to the Varnish log
with the tag ``VCL_Error``. If it fails during ``vcl_init``, then the
VCL load fails with the error message.

``fileread()`` does not do any binary-to-text conversion, so the
contents of the file should be the raw binary data that you intend to
read into the BLOB. The file should not, for example, contain a hex or
base64 encoding.

If you intend to read sensitive data from a file, consider setting the
file's permissions so that only the owner of the Varnish child process
can read it. Secure management of the file becomes part of your
administrative responsibility that is outside the scope of Varnish and
this VMOD.

``fileread()`` can be used to read data from a file in any VCL
subroutine, and the use case does not have to be related to
cryptography, but it is very inefficient. It reads from file and
allocates memory on every invocation, and that memory is wiped and
freed at the end of every task scope in which it is invoked, all of
which can have a severe impact on performance. See the ``fileread()``
funciton of the std VMOD (vmod_std(3)) for an efficient way to read
from files.

Example::

  # Write 16 bytes of binary data to a file.
  $ echo 'AAECAwQFBgcICQoLDA0ODw==' | base64 -d > /path/to/key

  # Make the file readable only to the Varnish worker process owner.
  $ chown vcache:varnish /path/to/key
  $ chmod 400 /path/to/key

  # In VCL, use the file contents as an encryption key.
  sub vcl_init {
      new aes128 = gcrypt.symmetric(AES128, CTR,
                                    key=gcrypt.fileread("/path/to/key"));
  }

.. _gcrypt.random():

BLOB random([ENUM quality], BYTES n)
------------------------------------

::

   BLOB random(
      [ENUM {STRONG, VERY_STRONG, NONCE} quality],
      BYTES n=0
   )

Return a BLOB containing ``n`` bytes of pseudo-random data. The
cryptographic strength of random number generation is determined by
the ``quality`` ENUM:

* ``NONCE`` (for "number used once"): Pseudo-random bytes for
  applications where cryptographic security is not required. This
  level is suitable, for example, for generation of initialization or
  counter vectors when used with modes of operation that require
  uniqueness, but do not have strong requirements for unpredictabilty.

* ``STRONG``: From the libgcrypt manual: "Use this level for session
  keys and similar purposes". Most applications requiring
  cryptographically secure pseudo-random data should use this level.

* ``VERY_STRONG``: From the libgcrypt manual: "Use this level for long
  term key material".

*NOTE: At the VERY_STRONG level, the random() function may take
several minutes to execute.* This level should *not* be used when
performance is important; ``STRONG`` is sufficient for crypto-quality
randomness. If at all, ``VERY_STRONG`` might be used in a service that
occasionally generates new keys, on the understanding that this will
take quite some time (and it may be necessary to set long timeouts).

Consider using the ``NONCE`` level for applications that do not
require strong randomness. With ``NONCE``, performance is better,
nothing can be revealed about the internal state of the strong random
number generator, and entropy mixed into the strong generator is not
consumed.

If ``n`` is 0B, then ``random()`` returns the same BLOB that was
returned by its most recent invocation in the same client or backend
context. Since 0B is the default value of ``n``, it can be left out
for this purpose, and the ``quality`` ENUM can be left out as well. In
other words, calling ``random()`` with no arguments returns the same
BLOB that was returned with arguments in the same client or backend
scope.

This makes it possible, for example, to use ``random()`` with
appropriate arguments to generate an initialization or counter vector
in an encryption operation, and then call ``random()`` again with no
arguments to assign the same value to a header to be sent along with
the encrypted message, as illustrated below.

The ``random()`` function fails if:

* ``n`` is 0B, but ``random()`` was not previously called with ``n`` >
  0B in the same client or backend context.

* ``n`` is greater than 0B, but the ``quality`` ENUM is not set.

* There is insufficient workspace for the BLOB to be returned.

If ``random()`` fails in ``vcl_init``, then the VCL load fails with an
error message. If it fails in any other subroutine, then it returns
NULL, and an error message is written to the log with the
``VCL_Error`` tag.

Example::

  # Assume the aes192 object shown in the examples above: AES-192
  # encryption with CBC mode.

  # Encrypt the contents of the X-Msg response header, using the
  # random() function to generate an initialization vector, which
  # is sent in the reponse header X-IV.

  sub vcl_resp {
      # The length of the IV MUST match the block size of the
      # cipher in use -- 128 bits (16 bytes) for AES. For CBC, the IV
      # MUST be unpredictable, so we use quality level STRONG.
      set resp.http.X-Encrypted
          = blob.encode(BASE64,
              aes192.encrypt(blob.decode(encoded=resp.http.X-Msg),
                             iv=gcrypt.random(STRONG, 16B)));

      # Now call random() with no arguments to retrive the IV that
      # was generated, to be sent in the base64-encoded response
      # header X-IV.
      set resp.http.X-IV = blob.encode(BASE64, gcrypt.random());

      # Remove the plaintext from the response header.
      unset resp.http.X-Msg;
  }

.. _gcrypt.random_int():

INT random_int(ENUM {STRONG, NONCE} quality, INT bound=0)
---------------------------------------------------------

Returns a random integer, using the random number generator with the
quality level specified by the ENUM ``quality``. These are the same
levels that are described for the ``random()`` function above, except
that the ``VERY_STRONG`` level is not available.

If ``bound`` is less than or equal to 0, then the return value lies
within the entire possible range for an VCL_INT (-999999999999999
.. 999999999999999).  The default value of ``bound`` is 0.

If ``bound`` is greater than 0, then the return value lies between 0
(inclusive) and ``bound`` (exclusive). To get a random integer between
values ``min`` and ``max`` inclusive with the ``NONCE`` generator, use
this formula::

  gcrypt.random_int(NONCE, (max - min) + 1) + min

Example::

  # Assign a random group number from 0 to 99 to a request.
  set req.http.X-Group = gcrypt.random_int(NONCE, 100);

.. _gcrypt.random_real():

REAL random_real(ENUM {STRONG, NONCE})
--------------------------------------

Returns a random REAL that is greater than or equal to 0.0 and less
than 1.0, using the random number generator with the specified quality
level. The permitted levels are ``NONCE`` and ``STRONG`` as described
above.

To get a random REAL in the range from ``min`` to ``max`` inclusive
with the ``STRONG`` generator, use this formula::

  gcrypt.random_real(STRONG) * (max - min) + min

Note that when a REAL is converted to a STRING, for example when it is
assigned to a header, the representation may be rounded up to
``"1.0"`` if the REAL's value is very close to 1.0. So it may appear
as if the range of ``random_real()`` includes 1.0, but this is just an
artifact of string conversion.

Example::

  # Assign an unpredictable REAL from -1.0 to 1.0 to a request.
  set req.http.X-Real = gcrypt.random_real(STRONG) * 2 - 1;

.. _gcrypt.random_bool():

BOOL random_bool(ENUM {STRONG, NONCE})
--------------------------------------

Returns a random boolean using the randomness generator with the
specified quality level. The permitted levels are ``NONCE`` and
``STRONG`` as described above.

This function is more efficient than, for example, calling
``random_int()`` with the bound set to 2 and then checking if the
result is 0 or 1 (or something similar with ``random_real()``), since
``random_bool()`` calls the randomness generators less often (caching
random bytes as bitmaps), and only uses one bit of randomness for each
invocation.

Example::

  # Choose from two courses of action unpredictably.
  if (gcrypt.random_bool(STRONG)) {
      call do_this;
  }
  else {
      call do_that;
  }

.. _gcrypt.wipe():

VOID wipe(BLOB)
---------------

Overwrites the memory region denoted by the BLOB, leaving it with all
zeroes.

The BLOB MUST be non-empty. If ``wipe()`` is called with an empty
BLOB, then an error message is written to the log with the
``VCL_Error`` tag.  If this happens in ``vcl_init``, then the VCL load
fails with the error message.

Example::

  # After setting a symmetric encryption key, which will be stored in
  # secure memory, wipe the BLOB from which the key was read.  This
  # ensures that the key is only stored in secure memory.
  sub vcl_init {
      new k = blob.blob(HEX, "000102030405060708090a0b0c0d0e0f");
      new aes = gcrypt.symmetric(AES, CTR, key=k.get(), secure=true);
      gcrypt.wipe(key.get());
  }

.. _gcrypt.version():

STRING version()
----------------

Returns the version string for this VMOD.

Example::

        std.log("Using VMOD gcrypt version " + gcrypt.version());

.. _gcrypt.gcrypt_version():

STRING gcrypt_version()
-----------------------

Returns the version string for the ``libgcrypt`` library with which
the VMOD is linked.

Example::

        std.log("Using libgcrypt version " + gcrypt.gcrypt_version());

REQUIREMENTS
============

This VMOD requires Varnish version 5.1 or later, and libgcrypt version
1.6.3 or later. It has been tested successfully against libgcrypt
versions 1.6.3 through 1.7.7, except for version 1.7.4. (Version 1.7.4
had a bug and was replaced by 1.7.5 soon after its release.)

LIMITATIONS
===========

As noted above, the VMOD uses Varnish workspace for the BLOB objects
returned by the ``.encrypt()`` and ``.decrypt()`` methods, and for
padding a plaintext before encryption, if necessary. If you find that
these methods are failing, with ``VCL_Error`` messages "out of space"
in the Varnish log, then increase the varnishd parameters
``workspace_client`` and/or ``workspace_backend``.

If you choose to use the libgcrypt secure memory feature, then the
size of the pool that is needed depends on the number of VMOD objects
that you create and the number of threads in which encryption
operations are performed. For each object in each thread, an internal
structure for libgcrypt is created the first time an encryption or
decryption is attempted; the same structure is then re-used for all
other operations in the same thread. These structures are deallocated
when the thread exits.

If secure memory has been specified for the VMOD object, then these
internal structures are allocated from the secure memory pool. So you
may need to configure a large pool if you are running many worker
threads to handle a heavy load for Varnish, and more so if you are
using many objects.

Recall that the number of worker threads run by Varnish is controlled
by the varnishd parameters ``thread_pools``, ``thread_pool_min`` and
``thread_pool_max``; this in turn affects how much secure memory will
need to be available for each object in each thread. ``thread_pools``
determines how many thread pools there are, and for each pool, at
least ``thread_pool_min`` threads will always be started. More threads
may be started to respond to increasing load, up to the maximum given
by ``thread_pool_max`` for each thread. As load decreases (that is,
when threads become idle for time given by ``thread_pool_timeout``),
threads will be stopped until the minimum ``thread_pool_min`` is
reached.

The VMOD uses pthread keys (see ``pthread_key_create(3)``) to locate
the thread-specific data for a symmetric encryption object. A key must
be created for each object in each active VCL instance, and the number
of keys that can be created is subject to the limit defined by
``PTHREAD_KEYS_MAX`` (1024 for current Linux versions); so this limits
the number of ``symmetric`` objects that can exist in active VCL
instances. When an object is de-allocated, its key is deleted, and
then no longer counts against the maximum.

Note that when a VCL instance is unloaded at runtime (so as to load a
new instance), the objects allocated for the unloaded instance are not
de-allocated until that instance enters the cold state, by default
after 5 minutes in Varnish 5.1 (determined by the varnishd parameter
``vcl_cooldown``); so the number of pthread keys accumulates during
this time. You can bring about an earlier de-allocation, and hence
reduce the use of the pthread keys, by shortening the ``vcl_cooldown``
period, or by using the CLI command ``vcl.discard`` to explicitly
remove previous VCL instances.

SECURITY
========

If you intend to use the VMOD to protect sensitive data, it is
critical that you have a firm understanding of how to use the
cryptographic primitives that it makes available, and of potential
vulnerabilities of the tools you employ -- libgcrypt, Varnish, VMOD
gcrypt and any other VMODs you deploy.

Software
--------

Both libgcrypt and Varnish have strong security records. Nevertheless,
you should stay informed of any security fixes that are released for
both packages, and install them promptly when they are. Please inform
us (at the developer contact given in `SEE ALSO`_) if you encounter
any difficulties using the VMOD with new versions of libgcrypt or
Varnish.

VMOD gcrypt, in contrast, is much newer, and has not established a
security record. We welcome any bug reports, bug fixes, reviews or
other feedback you may have. And it would help to let us know if
you are using the VMOD successfully.

Secure and non-secure memory
----------------------------

The secure memory feature of the libgcrypt library gives a certain
measure of protection against exposure of sensitive data, but at
present that is limited to internal structures used by the library
(mainly, it protects the keys stored in those structures). Moreover,
the secure memory pool is visible in the address space of the Varnish
child process.

VMOD gcrypt, by itself, is neutral as to the sources of data that
enter into cryptographic operations -- keys, plaintexts, ciphertexts,
IVs and counters. But these are typically obtained from sources that
are readable from other elements of Varnish.

As can be seen in the examples above that use VMOD blob to create
key objects, the contents of a key are readable in the VCL source.  If
you are using the VMOD that way, consider storing VCL sources so that
they are only readable by the owner of the Varnish child process.

Ciphertexts and plaintexts are typically obtained from sources that
are stored in Varnish workspaces, for example when they are read from
headers, as in the examples above. And the ciphertexts and plaintexts
produced by the VMOD on encryption and decryption are stored in
workspaces. Workspaces are re-used for the lifetime of a worker
thread. They tend to be overwritten, but this does not happen
automatically when a workspace is re-used. Nothing prevents another
VMOD from reading the workspace used by VMOD gcrypt.

Note that if any such data is stored in the cache, for example in
response headers of cached responses, then they are saved for the
lifetime of the cached object. If file storage is used for the cache,
then that data can be read from the cache file.

All of the data just described, *except* for the contents of the
libgcrypt secure memory pool, could conceivably be paged into swap
space, and all of it can be written to a core dump file.

Therefore, if you are working with sensitive data, consider the
following measures:

* Test your Varnish deployment thoroughly with test data, until you
  are confident that it does not swap or dump core.

* Then in production, use the means of your operating system to ensure
  that the Varnish child process does not swap or write core files.

* Examine any other VMODs that you use to ensure that they do not leak
  data from workspaces.

* Consider using the ``fileread()`` function to read sensitive data
  such as cryptographic keys from files that are only readable by the
  Varnish worker process. If you must expose sensitive data in VCL
  sources, ensure that those sources are only readable by the worker
  process.

* If you are using file storage for the cache, ensure that the cache
  file is only readable and writable by the child process.

Cryptographic good practice
---------------------------

Finally, make sure that you are using the cryptographic primitives
properly and safely. libgcrypt provides cryptographic building blocks,
and the VMOD makes some of these available in VCL. But neither the
library nor the VMOD can prevent you from using those building blocks
improperly, thus undermining the security of your application.

The proper use of cryptography is a subject that is beyond the scope
of this manual; when in doubt, consult an expert. We want to emphasize
the following points, but this list is by no means exhaustive:

* The ECB mode of operation is included for testing purposes, but
  should *never* be used to protect sensitive data in insecure
  environments.

* It is important to fulfill the uniqueness and predictability
  requirements for the initialization and counter vectors used for
  symmetric encryption, which depend in part on the mode of
  operation. IVs and counters MUST be unique in all cases; they must
  never be re-used with the same encryption key. For CBC mode, the IV
  must also be unpredictable; for example, the ``STRONG`` quality
  level should be used if the ``random()`` function is used to
  generate IVs for CBC. For the other modes, unpredictability is not
  required, and the ``NONCE`` level is sufficient.

* libgcrypt permits certain operations that are poor practice, such as
  setting an init vector whose length is not equal to the block length
  of a symmetric cipher, but emits a warning message. The VMOD follows
  the library in allowing these operations to proceed, but you should
  check the Varnish log for the warnings, and correct any such
  problems.

* Make sure that you have a secure procedure in place for generating
  and storing cryptographic keys, and for changing the keys
  periodically.

INSTALLATION
============

See `INSTALL.rst <INSTALL.rst>`_ in the source repository.

SEE ALSO
========

* varnishd(1)
* vcl(7)
* varnishlog(1)
* vmod_std(3)
* mlock(2)
* pthread_key_create(3)
* libgcrypt: https://gnupg.org/software/libgcrypt/index.html
* source repository: https://code.uplex.de/uplex-varnish/libvmod-gcrypt
* developer contact: <varnish-support@uplex.de>, and at the source
  repository site

COPYRIGHT
=========

::

  This document is licensed under the same conditions
  as the libvmod-gcrypt project. See LICENSE for details.
 
  Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>