OpenSSL Support
***************

SXEmacs can be linked with OpenSSL libcrypto and libssl to provide a comprehensive gateway to cryptographic and related functions.

Note: Currently the API provided for SXEmacs is experimental. Conceptional changes, renaming, and changes in the behaviour of functions are highly likely.

Building SXEmacs with OpenSSL support
=====================================

SXEmacs OpenSSL support requires linking to the OpenSSL libcrypto and libssl libraries.  We recommend the most recent stable version, which is 0.9.8b as of writing this documentation.  Nonetheless, we have successfully tested each version since 0.9.6d (May 2002).  OpenSSL can be obtained at

     http://www.openssl.org

For most systems the following snippet can be used to build a reasonably feature-rich and yet fast library:

     ./config --prefix=/usr/local/ threads enable-mdc2 enable-gmp \
     enable-rc5 enable-shared enable-zlib enable-zlib-dynamic enable-dso \
     enable-krb5 enable-asm

Some of the above options depend in turn on external resources, like GMP and zlib.  However, describing in detail how to build and install OpenSSL is beyond the scope of this document.  See the OpenSSL manual for details.

If you plan to build SXEmacs with OpenSSL support you need to install OpenSSL first.  On some systems, OpenSSL will come pre-installed in /usr.  In this case, all you need to do is passing --with-openssl to configure.

If the configure script fails to detect your installation of OpenSSL, make sure you supplied header files, or try to explicitly name the location of your OpenSSL installation via the --site-prefixes flag when you run the SXEmacs configure script.

SXEmacs OpenSSL API
===================

The SXEmacs OpenSSL API is intended to be an exact copy of the OpenSSL API functions in libcrypto and libssl.  The intent is to provide elisp for everything you have told your OpenSSL library to contain.  Then, at stage 2, let higher level Lisp code come up with policies and remove the guts of libcrypto and libssl.

Despite this rather noble intention of giving access to anything in OpenSSL there are conceptional limits.  Auxiliary stuff from OpenSSL such as BIO or BN will not be accessible to elisp, though the C implementation might use them directly or indirectly as well.

On the one hand this is due to their very special purpose (like finding prime numbers), on the other hand due to their obsoleteness (like BIO_gets, BIO_do_connect, etc.) when SXEmacs already provides a similar or more powerful implementation.

For convenience, we denote the elisp implementation of OpenSSL with openssl whereas the distributed library package from http://www.openssl.org is denoted OpenSSL.

General information
-------------------

In this section we deal with informative functions which kind of reflect the underlying library capabilities.  It is very hard to say which of the capabilities are guaranteed to exist since it is possible to strip certain cryptographic stuff from the OpenSSL installation, for instance due to license, patent, or legal issues.

 -- Function: ossl-version
     Return a descriptive version number of the OpenSSL in use.

     The version string should be identical to the output of openssl version in a shell.

          (ossl-version)
            => "OpenSSL 0.9.9-dev XX xxx XXXX"

 -- Function: ossl-available-digests
     Return a list of digest algorithms in the underlying crypto library. This yields a plain list of symbols.

 -- Function: ossl-available-ciphers
     Return a list of cipher algorithms in the underlying crypto library. This yields a list of symbols.

     (ossl-available-digests)
       => '(UNDEF MD2 MD5 RSA-MD2 RSA-MD5 SHA RSA-SHA SHA1 RSA-SHA1 DSA-SHA
            DSA-SHA1-old MDC2 RSA-MDC2 DSA-SHA1 RSA-SHA1-2 DSA RIPEMD160
            RSA-RIPEMD160 MD4 RSA-MD4 ecdsa-with-SHA1 RSA-SHA256 RSA-SHA384
            RSA-SHA512 RSA-SHA224 SHA256 SHA384 SHA512 SHA224 whirlpool)

     (ossl-available-ciphers)
       => '(RC4 DES-ECB DES-CFB DES-CBC DES-EDE DES-EDE3 IDEA-CBC IDEA-CFB
            IDEA-ECB RC2-CBC RC2-ECB RC2-CFB RC2-OFB DES-EDE-CBC
            DES-EDE3-CBC DES-OFB IDEA-OFB DES-EDE-CFB DES-EDE3-CFB
            DES-EDE-OFB DES-EDE3-OFB DESX-CBC BF-CBC BF-ECB BF-CFB BF-OFB
            RC4-40 RC2-40-CBC CAST5-CBC CAST5-ECB CAST5-CFB CAST5-OFB
            RC5-CBC RC5-ECB RC5-CFB RC5-OFB RC2-64-CBC AES-128-ECB
            AES-128-CBC AES-128-OFB AES-128-CFB AES-192-ECB AES-192-CBC
            AES-192-OFB AES-192-CFB AES-256-ECB AES-256-CBC AES-256-OFB
            AES-256-CFB AES-128-CFB1 AES-192-CFB1 AES-256-CFB1 AES-128-CFB8
            AES-192-CFB8 AES-256-CFB8 DES-CFB1 DES-CFB8 CAMELLIA-128-CBC
            CAMELLIA-192-CBC CAMELLIA-256-CBC CAMELLIA-128-ECB
            CAMELLIA-192-ECB CAMELLIA-256-ECB CAMELLIA-128-CFB
            CAMELLIA-192-CFB CAMELLIA-256-CFB CAMELLIA-128-CFB1
            CAMELLIA-192-CFB1 CAMELLIA-256-CFB1 CAMELLIA-128-CFB8
            CAMELLIA-192-CFB8 CAMELLIA-256-CFB8 CAMELLIA-128-OFB
            CAMELLIA-192-OFB CAMELLIA-256-OFB)

These two functions are most useful to conditionalise like

     (when (member 'MD4 (ossl-available-digests))
       ...)

The aforementioned functions work at run-time (not compile time) so it is possible when building a dynamically linked SXEmacs to update OpenSSL on the fly.  A very rough estimate is to assume to have support for the MD5 and SHA1 message digests, and the BF-* symmetric cipher systems, any installation of OpenSSL without those is purely ridiculous although not impossible.

Furthermore we provide convenience functions which, given a digest or cipher algorithm symbol, allow to obtain information about the hash result size of a digest, and the key size of a cipher, respectively.

 -- Function: ossl-digest-size digest
     Return the hash length of DIGEST in bytes.

          (ossl-digest-size 'MD5)
            => 16
          (ossl-digest-size 'SHA512)
            => 64

 -- Function: ossl-digest-block-size digest
     Return the block size of DIGEST in bytes.

          (ossl-digest-block-size 'MD5)
            => 64
          (ossl-digest-block-size 'SHA512)
            => 128

 -- Function: ossl-cipher-key-length cipher
     Return the effective key size of CIPHER in bytes.

          (ossl-cipher-key-length 'RC4)
            => 16
          (ossl-cipher-key-length 'CAMELLIA-256-CBC)
            => 32

 -- Function: ossl-cipher-iv-length cipher
     Return the initialisation vector length of CIPHER in bytes.

          (ossl-cipher-iv-length 'idea-cbc)
            => 8
          (ossl-cipher-iv-length 'aes-256-cbc)
            => 16

 -- Function: ossl-cipher-block-size cipher
     Return the block size of CIPHER in bytes.

          (ossl-cipher-block-size 'aes-256-cbc)
            => 16
          (ossl-cipher-block-size 'rc4)
            => 1

 -- Function: ossl-cipher-mode cipher
     Return the operation mode of CIPHER.

          (ossl-cipher-mode 'rc4)
            => stream
          (ossl-cipher-mode 'aes-192-ofb)
            => ofb

The openssl API provides following features:

  * 'openssl when fundamental functions are available

  * 'openssl-rsa when RSA keys are available

  * 'openssl-dsa when DSA keys are available

  * 'openssl-ec when elliptic curves are available

  * 'openssl-dh when DH keys are available

  * 'openssl-ssl when the TLS/SSL and X509 capabilities are available

(Pseudo) Random Numbers
-----------------------

Random numbers are necessary for cryptographically secure implementations.  The term number here indeed means a string.  OpenSSL itself provides random number generators which fulfill the the demands of cryptography.

 -- Function: ossl-rand-bytes count
     Return COUNT bytes of randomness.

     Note: You probably want to put a wrapping encoder function (like base16-encode-string) around it, since this returns binary string data.

     (base16-encode-string (ossl-rand-bytes 8))
       => "5a78acd572984bdf"

Modern systems supply more sophisticated sources for random data, so called entropy gathering daemons.

 -- Function: ossl-rand-bytes-egd count egd
     Return COUNT bytes of randomness from an EGD socket. By default use the socket /var/run/egd-pool.

     Note: You probably want to put a wrapping encoder function (like base16-encode-string) around it, since this returns binary string data.

     (base16-encode-string (ossl-rand-bytes-egd 8 "/var/run/egd-pool"))
       => "59342a240b356a04"

Please note that the system's random sources are used only for seeding OpenSSL's pseudo-random number generator.  So even large amounts of random data should be feasible.  In contrast querying for large amounts of random data directly most likely freezes your process since the size of random devices or pools, and hence the size of cached random data, is quite limited.

Random numbers generated this way, can be used as ``passwords'' or salt values in various encryption and decryption functions.  As stated above, whenever security is concerned one of the above functions, ossl-rand-bytes or ossl-rand-bytes-egd should be used to obtain random numbers.  The built-in random function of SXEmacs is not cryptographically secure.

Message Digests (aka hashes)
----------------------------

Message digests are widely used in modern information infrastructure.  They are derived from (collision free) one-way hash functions.

A hash function (such as md5 or sha1) is a function with following properties:

 1. reduction: data of arbitrary length is mapped onto data of fixed length

 2. dispersion: a change of one bit in input data changes (ideally) half the bits of the hash value.

 3. well definedness: computing a hash value from the same source data twice yields the same result

 4. efficiency: computing hash values is efficient (ideally with complexity O(n)) on the input, but it is hard to compute a preimage for a given hash value.

Often, the last property is too weak in practice, therefore most hash functions comply with the even stronger:

  * collision-freeness: it is hard to compute two different source data which result in the same hash value.

Message digests fulfill several tasks in daily use.  Most commonly used are so called checksums.  In modern days hash functions are used almost exclusively for their error detecting facilities in contrast to other checksum algorithms like CRC32.

Beyond that, message digests play an important role in digital signatures.  Since public key crypto systems map long plaintexts on long ciphertexts, message digests are used to obscure the length of a plaintext.

Therefore in digital signatures not the message itself is signed but the hash value of that message.  That also assures a certain upper bound of the length of a digital signature which is (as in real life) rather short compared to the message that was signed.

Okay, after this short introduction to message digests, here are the functions to access them from elisp.

 -- Function: ossl-digest digest string
     Return the message digest of STRING computed by DIGEST. DIGEST may be one of the OpenSSL digests you have compiled. See ossl-available-digests.

     Note: You probably want to put a wrapping encoder function (like base16-encode-string) around it, since this returns binary string data.

In order to compute digest sums from files without actually looking at the file contents explicitly, there is the companion function ossl-digest-file which works similarly.

 -- Function: ossl-digest digest file
     Return the message digest of the contents of FILE computed by DIGEST. DIGEST may be one of the OpenSSL digests you have compiled. See ossl-available-digests.

     Note: You probably want to put a wrapping encoder function (like base16-encode-string) around it, since this returns binary string data.

The current implementation of the OpenSSL API in SXEmacs uses the EVP layer of OpenSSL to access the digests.

     (base16-encode-string (ossl-digest 'md5 "hash me"))
       => "17b31dce96b9d6c6d0a6ba95f47796fb"

     (base16-encode-string (ossl-digest 'SHA1 "hash me"))
       => "43f932e4f7c6ecd136a695b7008694bb69d517bd""

Let's do some performance tests.

     ;; this is the SXEmacs built-in implementation of MD5
     (let ((st (current-btime)))
       (dotimes (i 100000)
         (md5 "Some test string to hash"))
       (- (current-btime) st))
       => 6194289
       ;; time in microseconds, so this is about 6 seconds

     ;; now compare to the OpenSSL implementation
     (let ((st (current-btime)))
       (dotimes (i 100000)
         (ossl-digest 'md5 "Some test string to hash"))
       (- (current-btime) st))
       => 10589408
       ;; which is about 10 seconds

As we can see, the built-in implementation has slightly better performance when hashing short strings.  The following example shows performance on long strings, like the buffer string here.

     (length (buffer-string))
       => 16861

     ;; we begin with the built-in implementation
     (let ((st (current-btime))
           (b (buffer-string)))
       (dotimes (i 100000)
         (md5 b))
       (- (current-btime) st))
       => 74350982
       ;; which is about 74 seconds

     ;; compare to the OpenSSL API
     (let ((st (current-btime))
           (b (buffer-string)))
       (dotimes (i 100000)
         (base16-encode-string
           (ossl-digest 'md5 b)))
       (- (current-btime) st))
       => 31697926
       ;; which is about 31 seconds

This latter example shows digest hashing ``under real conditions'' since in practice messages to be hashed are typically in the range of 1000 to 30000 characters.  This range is even vastly exceeded when dealing with checksums for files.

Since the built-in md5 implementation cannot handle file streams, we have to turn them into strings.  A possible way to achieve this has been suggested by Steve Youngs.  I shall illustrate it with a tarball file.

     freundt@muck:~> ls -sh ~/temp/pdftex-1.30.3.tar.bz2
     3.2M /home/freundt/temp/pdftex-1.30.3.tar.bz2

     (let ((st (current-btime))
           (b (with-temp-buffer
                (insert-file-contents-literally
                 "~/temp/pdftex-1.30.3.tar.bz2")
                (buffer-string))))
       (dotimes (i 100)
         (md5 b))
       (- (current-btime) st))
       => 22729718
       ;; which is about 22 seconds

Compared to the file stream function ossl-digest-file:

     (let ((st (current-btime)))
       (dotimes (i 100)
         (ossl-digest-file 'md5 "~/temp/pdftex-1.30.3.tar.bz2"))
       (- (current-btime) st))
       => 4189695
       ;; which is about 4 seconds

Another performance test which compares the elisp implementation of sha1 (taken from `No Gnus v0.4') to the one from the OpenSSL API

     (let ((st (current-btime)))
       (dotimes (i 500)
         (sha1-binary "a short test string"))
       (- (current-btime) st))
       => 2574326
       ;; which is about 2.5 seconds

       ;; the same with the OpenSSL API
     (let ((st (current-btime)))
       (dotimes (i 500)
         (ossl-digest 'sha1 "a short test string"))
       (- (current-btime) st))
       => 31378
       ;; which is about 0.03 seconds

These results suggest to always use the openssl interface in favour of other implementations.

Message Authentication Codes (aka keyed hashes)
-----------------------------------------------

Ordinary message digests only offer data integrity verification, while HMACs may be used to simultaneously verify both the data integrity and the authenticity of a message.  This is accomplished by using a secret key.  Now whenever two parties have agreed upon a common secret key, one of them can verify that a message hash was indeed computed by the other one.

 -- Function: ossl-hmac digest message password
     Return the message authentication code of MESSAGE using the hash function DIGEST and the key PASSWORD.

     Note: You probably want to put a wrapping encoder function (like base16-encode-string) around it, since this returns binary string data.

Unlike in public-key cryptography, this technique requires a new secret key for any two parties which want to communicate.  On the other hand, this technique works symmetrically, that is the same function can be used for both generating and verifying a keyed message digest.

     (base16-encode-string
      (ossl-hmac 'SHA512 "string to hash" "secret"))
       => "62351dfae2030fb28058a2aeba6ce3597d803575c120109ed6cfee240d7
           50e71ffff1d8dfc8d52e666549dcb6ba95fb4d550bdc9f31178c19fecb4
           30ddb7b565"

     ;; now hashing with a false password
     (base16-encode-string
      (ossl-hmac 'SHA512 "string to hash" "false"))
       => "44afb8f67ea7f66693e891e79b7295569163e3e6faebd47d2a63e564778
           c72221cbb4cdff01ff1052ea98d2058f33c1ecf48f0c45bb64e526a81d8
           f389436ab0"

Symmetric Cryptography
----------------------

Symmetric-key algorithms can be divided into stream ciphers and block ciphers.  Stream ciphers encrypt the bits of the message one at a time, and block ciphers take a number of bits and encrypt them as a single unit.

In order to use symmetric-key cryptography some preparations have to be done, mostly due to the block-oriented operation of the algorithms. The following function, given a cipher and digest algorithm, computes a valid key suitable for the given cipher algorithm.

 -- Function: ossl-bytes-to-key cipher digest salt password count
     Derive a key and initialisation vector (iv) suitable for a cipher. Return a string KEY being the key. The initialisation vector is put into KEY's property list as 'iv.

     CIPHER (a symbol) is the cipher to derive the key and IV for. Valid ciphers can be obtained by ossl-available-ciphers.

     DIGEST (a symbol) is the message digest to use. Valid digests can be obtained by ossl-available-digests.

     SALT (string or nil) is used as a salt in the derivation. Use nil here to indicate that no salt is used.

     PASSWORD is an arbitrary string which is processed to derive a unique key and IV.

     COUNT (a positive integer) is the iteration count to use. This indicates how often the hash algorithm is called recursively.

     Note: You probably want to put a wrapping encoder function (like base16-encode-string) around it, since this returns binary string data.

Note: It is disregarded to use the key/iv pair of, say, AES-128 cipher for e.g. a blowfish (BF) cipher, although it seems possible and is not explicitly forbidden.  Such malpractices may result in severe crashes.

     (base16-encode-string
      (ossl-bytes-to-key 'AES-256-OFB 'SHA512 "somesalt" "secret" 1))
       => "bd2b1aaf7ef4f09be9f52ce2d8d599674d81aa9d6a4421696dc4d93dd0619d68"

     (base16-encode-string
      (ossl-bytes-to-key 'AES-256-OFB 'SHA512 "diffsalt" "secret" 1))
       => "bd2b1aaf7ef4f09be9f52ce2d8d599674d81aa9d6a4421696dc4d93dd0619d68"

     (base16-encode-string
      (ossl-bytes-to-key 'AES-256-OFB 'SHA512 "diffsalt" "retsec" 1))
       => "38515c1868bcab470075ec32bc79b0ed1aa945de95d2261991ea840921e7747b"

These examples show how different passwords yield different keys, and that different salts do not affect the result.  As mentioned in the doc string the result carries an object-plist with the initialisation vector inside:

     (object-plist
      (ossl-bytes-to-key 'AES-256-OFB 'SHA512 "somesalt" "secret" 1))
       => (iv "????????????????")

     (base16-encode-string
      (get
       (ossl-bytes-to-key 'AES-256-OFB 'SHA512 "somesalt" "secret" 1)
       'iv))
       => "2ce56b4d64a9ef097761ced99e0f6726"

 -- Function: ossl-encrypt cipher string key &optional iv
     Return the cipher of STRING computed by CIPHER under KEY.

     CIPHER (a symbol) may be one of the OpenSSL cipher algorithms you have compiled. See ossl-available-ciphers.

     STRING is the text to be encrypted.

     KEY should be a key generated suitably for this cipher, for example by ossl-bytes-to-key.

     Optional fourth argument IV should be an initialisation vector suitable for this cipher. Normally the initialisation vector from KEY's property list is used. However, if IV is non-nil, use this IV instead.

     Note: You probably want to put a wrapping encoder function (like base16-encode-string) around it, since this returns binary string data.

 -- Function: ossl-decrypt cipher string key &optional iv
     Return the deciphered version of STRING computed by CIPHER under KEY.

     CIPHER (a symbol) may be one of the OpenSSL cipher algorithms you have compiled. See ossl-available-ciphers.

     STRING is the text to be decrypted.

     KEY should be a key generated suitably for this cipher, for example by ossl-bytes-to-key.

     Optional fourth argument IV should be an initialisation vector suitable for this cipher. Normally the initialisation vector from KEY's property list is used. However, if IV is non-nil, use this IV instead.

     (base16-encode-string
      (let ((key
             (ossl-bytes-to-key 'AES-256-OFB 'SHA512 "salt" "secret" 10)))
        (ossl-encrypt 'AES-256-OFB "Very secret text." key)))
       => "bbedc78c88eddea29c8653f551da391091"

     (let ((key
            (ossl-bytes-to-key 'AES-256-OFB 'SHA512 "salt" "secret" 10))
           (s (base16-decode-string "bbedc78c88eddea29c8653f551da391091")))
       (ossl-decrypt 'AES-256-OFB s key))
       => "Very secret text."

     (let ((key
            (ossl-bytes-to-key 'AES-256-OFB 'SHA512 "salt" "dontknow" 1))
           (s (base16-decode-string "bbedc78c88eddea29c8653f551da391091")))
       (ossl-decrypt 'AES-256-OFB s key))
       => "?????????????????"

The above example shows a complete cycle of encryption and decryption, as well as an attempt to decrypt a string using a wrong password.

As for message digests, there are two companion functions which directly work on files.

 -- Function: ossl-encrypt-file cipher file key &optional iv outfile
     Return the encrypted contents of FILE computed by CIPHER under KEY.

     CIPHER (a symbol) may be one of the OpenSSL cipher algorithms you have compiled. See ossl-available-ciphers.

     FILE is the file to be encrypted.

     KEY should be a key generated suitably for this cipher, for example by ossl-bytes-to-key.

     Optional fourth argument IV should be an initialisation vector suitable for this cipher. Normally the initialisation vector from KEY's property list is used. However, if IV is non-nil, use this IV instead.

     Optional fifth argument OUTFILE may specify a file to have the encrypted data redirected.

     Note: You probably want to put a wrapping encoder function (like base16-encode-string) around it, since this returns binary string data.

 -- Function: ossl-decrypt-file cipher file key &optional iv outfile
     Return the deciphered version of FILE computed by CIPHER under KEY.

     CIPHER (a symbol) may be one of the OpenSSL cipher algorithms you have compiled. See ossl-available-ciphers.

     FILE is the file to be decrypted.

     KEY should be a key generated suitably for this cipher, for example by ossl-bytes-to-key.

     Optional fourth argument IV should be an initialisation vector suitable for this cipher. Normally the initialisation vector from KEY's property list is used. However, if IV is non-nil, use this IV instead.

     Optional fifth argument OUTFILE may specify a file to have the decrypted data redirected.

public key crypto systems
-------------------------

While keys for symmetric ciphers can be easily stored as strings, keys for asymmetric algorithms are more complex.  The openssl API therefore provides a dedicated primitive type, called pkey.  Pkey objects can contain key pairs -- i.e. pairs of public keys and corresponding private keys, public keys, and also certificates.  They are general container object for various subtypes such as RSA, DSA, EC, and DH keys.

However, you cannot directly create such a pkey object nor access nor modify parts of it.  Pkey objects are actually meant as transporter objects to simplify those functions which need them.

 -- Function: ossl-pkey-p object
     Return t if OBJECT is a pkey, nil otherwise.

 -- Function: ossl-pkey-size pkey
     Return the size a public key PKEY in bits.

 -- Function: ossl-pkey-private-p pkey
     Return non-nil if PKEY contains private data.

     Note: This function is not native OpenSSL.

 -- Function: ossl-pkey-get-public pkey
     Return a copy of PKEY stripped by the private data.

     Note: This function is not native OpenSSL.

The above functions have a generic character because they are independent from the choice of algorithm, see below.  For each of these algorithms we provide a constructor function ossl-ALGO-generate-key and a subtype predicate, named ossl-ALGO-pkey-p.  In case of RSA and DSA we also support a subkey check.

Note: At the moment we do not provide the creation of DH-keys.

Now the first important asymmetric algorithm is RSA.  As mentioned in the general introduction OpenSSL can be built without it.  The following functions therefore exist iff (featurep 'openssl-rsa) evaluates to t.

 -- Function: ossl-rsa-generate-key bits exp
     Return an RSA public key with of length BITS and exponent EXP.

 -- Function: ossl-rsa-pkey-p pkey
     Return t iff PKEY is of RSA type.

 -- Function: ossl-rsa-subkey-p pkey1 pkey2
     Return t if PKEY1 is a subkey of PKEY2, nil otherwise, i.e. if PKEY1 has the same public key data as PKEY2 and PKEY2 has all private data.

     Note: This function is not native OpenSSL.

     (setq rsaexmpl (ossl-rsa-generate-key 2048 3))
       => #<OpenSSL RSA private/public key, size 2048>
     (ossl-rsa-pkey-p rsaexmpl)
       => t
     (ossl-pkey-private-p rsaexmpl)
       => t

     (setq rsapub (ossl-pkey-get-public rsaexmpl))
       => #<OpenSSL RSA public key, size 2048>
     (ossl-pkey-private-p rsapub)
       => nil
     (ossl-rsa-subkey-p rsapub rsaexmpl)
       => t

Another important asymmetric algorithm is DSA.  The presence of DSA support in the underlying library can be checked by (featurep 'openssl-dsa).  Nonetheless, it is very unlikely to face an OpenSSL installation without DSA or RSA.

 -- Function: ossl-dsa-generate-key bits &optional seed
     Return a DSA public key with of length BITS seeded with (optional) SEED.

 -- Function: ossl-dsa-pkey-p pkey
     Return t if PKEY is of DSA type, nil otherwise.

 -- Function: ossl-dsa-subkey-p pkey1 pkey2
     Return t if PKEY1 is a subkey of PKEY2, nil otherwise, i.e. if PKEY1 has the same public key data as PKEY2 and PKEY2 has all private data.

     Note: This function is not native OpenSSL.

     (setq dsaexmpl (ossl-dsa-generate-key 1024))
       => #<OpenSSL DSA private/public key, size 384>
     (ossl-dsa-pkey-p dsaexmpl)
       => t
     (ossl-pkey-private-p dsaexmpl)
       => t

     (setq dsapub (ossl-pkey-get-public dsaexmpl))
       => #<OpenSSL DSA public key, size 384>
     (ossl-pkey-private-p dsapub)
       => nil
     (ossl-dsa-subkey-p dsapub dsaexmpl)
       => t

Elliptic curve cryptography is quite new and is possibly missing on many systems.  Use (featurep 'openssl-ec) to check for elliptic curve support.  Another particularity of the ec system is that it is based on a fixed set of curves which can be referred to by name.  At the moment we do not support creating custom curves.

 -- Function: ossl-ec-available-curves
     Return a list of builtin elliptic curves.

 -- Function: ossl-ec-generate-key curve
     Return a EC public key on CURVE. CURVE may be any symbol from (ossl-ec-available-curves).

     Note: At the moment we do not support creating custom curves.

 -- Function: ossl-ec-pkey-p pkey
     Return t if PKEY is of EC type, nil otherwise.

     (ossl-ec-available-curves)
       => (Oakley-EC2N-4 Oakley-EC2N-3 wap-wsg-idm-ecid-wtls12
           wap-wsg-idm-ecid-wtls11 wap-wsg-idm-ecid-wtls10
           wap-wsg-idm-ecid-wtls9 wap-wsg-idm-ecid-wtls8
           wap-wsg-idm-ecid-wtls7 wap-wsg-idm-ecid-wtls6
           wap-wsg-idm-ecid-wtls5 wap-wsg-idm-ecid-wtls4
           wap-wsg-idm-ecid-wtls3 wap-wsg-idm-ecid-wtls1 c2tnb431r1
           c2pnb368w1 c2tnb359v1 c2pnb304w1 c2pnb272w1 c2tnb239v3
           c2tnb239v2 c2tnb239v1 c2pnb208w1 c2tnb191v3 c2tnb191v2
           c2tnb191v1 c2pnb176v1 c2pnb163v3 c2pnb163v2 c2pnb163v1 sect571r1
           sect571k1 sect409r1 sect409k1 sect283r1 sect283k1 sect239k1
           sect233r1 sect233k1 sect193r2 sect193r1 sect163r2 sect163r1
           sect163k1 sect131r2 sect131r1 sect113r2 sect113r1 prime256v1
           prime239v3 prime239v2 prime239v1 prime192v3 prime192v2
           prime192v1 secp521r1 secp384r1 secp256k1 secp224r1 secp224k1
           secp192k1 secp160r2 secp160r1 secp160k1 secp128r2 secp128r1
           secp112r2 secp112r1)

     (setq ecexmpl (ossl-ec-generate-key 'secp224r1))
       => #<OpenSSL EC private/public key, size 512>
     (ossl-ec-pkey-p ecexmpl)
       => t
     (ossl-pkey-private-p ecexmpl)
       => t

     (setq ecpub (ossl-pkey-get-public ecexmpl))
       => #<OpenSSL EC public key, size 512>
     (ossl-pkey-private-p ecpub)
       => nil

Finally, there is the DH key exchange.  Its presence can be checked by (featurep 'openssl-dh).  At the moment we do not provide constructor functions.

 -- Function: ossl-dh-pkey-p pkey
     Return t if PKEY is of DH type, nil otherwise.

Once you have a pkey object you can use it for encryption/decryption, and/or signing/verification.  Talking in OpenSSL hybrid encryption and decryption is referred to as sealing and opening, respectively.

 -- Function: ossl-seal cipher string pkey
     Return an envelope derived from encrypting STRING by CIPHER under PKEY with the hybrid technique.

     That is, create a random key/iv pair for the symmetric encryption with CIPHER and encrypt that key/iv asymmetrically with the provided public key.

     The envelope returned is a list (ENCRYPTED_STRING ENCRYPTED_KEY ENCRYPTED_IV) where ENCRYPTED_STRING is the (symmetrically) encrypted message ENCRYPTED_KEY is the (asymmetrically) encrypted random key ENCRYPTED_IV is the (asymmetrically) encrypted random iv

     Note: You probably want to put a wrapping encoder function (like base16-encode-string) around it, since this function returns binary string data.

 -- Function: ossl-open cipher string pkey ekey eiv
     Return the deciphered message STRING from an envelope obtained by ossl-seal.

       * CIPHER is the cipher to use (the same as in ossl-seal)

       * STRING is the encrypted message

       * PKEY is the private key

       * EKEY is the encrypted random key

       * EIV is the encrypted iv

In the following example we reuse the keys generated above.

     (let ((envl
            (ossl-seal 'AES-256-CBC "I do not want to tell" rsaexmpl)))
       (setq str (car envl)
             key (nth 1 envl)
             iv (nth 2 envl))
       (mapcar #'base16-encode-string envl))
       => ("0e6a38b28efea3ca4901b268c141d7ac23ed5f8fa598d23d9846fe3ec1
            47278e"
           "167911a73b0a228b24e78bdd37197ec95b21bed3bbd62d1915d8fac791
            7915fd49fdd9774e7906ca53ed3bf4fb20de8339e628d469a496f7351c
            06fddda49b71c90e73e31c406cfb0f0fb7411d1c9d49842603c45415cc
            3a8f660c728e8f05c6479d004f5068a7969294b4cc81e13dd257df37dc
            886b11266a3ccba576396d200ebb1a3e8f7185fdbc6de40b63964562f9
            1cbe39118a07415c030fd4c3e25bbe2a64b2ab635b2ef9a71a5ddeeaf0
            4a73d7cd04ad334d1de04228db5a9fb9aebfa6a9dc9d76af5ec329b360
            d1cd8da45868450a3bc5c41bba95a0ad74439f7d5edffcdf7dff09c296
            35ae13215be1ae55f5d2b5e97d6a4d523470eef050b07193"
           "ab458ccb46cbc092c31614e997cd176b")

     (ossl-open 'AES-256-CBC str rsaexmpl key iv)
       => "I do not want to tell"

The above example is nice but does not demonstrate the real power of hybrid encryption.  In the following example we reuse the public subkey rsapub of rsaexmpl from above.  Also you will notice that the random key/iv pair has changed and thus the resulting encrypted string is not the same.

     (let ((envl
            (ossl-seal 'AES-256-CBC "I do not want to tell" rsapub)))
       (setq str (car envl)
             key (nth 1 envl)
             iv (nth 2 envl))
       (mapcar #'base16-encode-string envl))
       => ("93a5b3f2eb5a2eaee44805150717bb325b4e90be947591cc46b7819d3f
            4ec284"
           "300b9ab5a79524fdcb40fbed6bae7e9c470baa0d230f9b97c9b35de442
            62f82b626ef7668329d34cffc3eeddf535a879e974825e984c7e045c0a
            526b3b58453ae55926af519400f32c4aee7115088068fcb6fc75ce78c5
            b6d61bbaf90f0c4aff1d83efd63c45c62989c29efda187bcbd94edf9f1
            427ec8dce22cd6333e8196120285dc5bb224b9d7e9ecfb23e016475706
            5da6f999560d010adaf0465b108b2a84989ff8bd17778b61875f633a35
            a02c2cc1fdf3a3e50ad4a5fb7ad9a05b1a3a1818a21f3d7c71a33949f6
            437ee64bee60e1ae92ebea43ca524b15344a7fc2712e9758b98f1b2c9d
            c8ad3d074486f0d35fece7bf7b6ce979fa760aa7bd5854b2"
           "6979d574e8bc10dfddefd4cb017186a2")

     (ossl-open 'AES-256-CBC str rsaexmpl key iv)
       => "I do not want to tell"

     ;; try with just the public part
     (ossl-open 'AES-256-CBC str rsapub key iv)
     error--> cannot open, key has no private key data

Also note you cannot use DSA keys for sealing.  They are exclusively for signatures.

Signing and verifying works similar to sealing and opening.  Instead of a cipher algorithm you need to specify a message digest algorithm. However, in this case the signature step requires a pkey with private data and the verification step can be done with only the public part of the key.

 -- Function: ossl-sign digest string pkey
     Return a signature obtained by signing STRING under DIGEST with PKEY.

     That is, hash the message STRING with the message digest DIGEST and encrypt the result with the private key PKEY.

     Note: Due to some relationship between the public key system and the message digest you cannot use every digest algorithm with every private key type. The most certain results will be achieved using RSA keys with RSA-* digests, DSA keys with DSA-* digests.

     See ossl-available-digests.

     Note: You probably want to put a wrapping encoder function (like base16-encode-string) around it, since this returns binary string data.

 -- Function: ossl-verify digest string sig pkey
     Return t iff SIG is a valid signature of STRING under DIGEST obtained by PKEY.

     That is, hash the message STRING with the message digest DIGEST, then decrypt the signature SIG with the public key PKEY.  Compare the results and return t iff both hashes are equal.

       * DIGEST is the digest to use (the same as in ossl-sign)

       * STRING is the message

       * SIG is the signature of message

       * PKEY is the public key

Again we reuse the keys defined above.

     (progn
       (setq sig (ossl-sign 'SHA1 "I owe you a beer" dsaexmpl))
       (base16-encode-string sig))
       => "302d021500c2e5197d266573216e4daa85e0a7e43424d0f031021451186
           24043517e0cd24f381d0e6c92f96198f297"

     (ossl-verify 'SHA1 "I owe you a beer" sig dsapub)
       => t

     ;; we try to fake the signed text
     (ossl-verify 'SHA1 "I owe you 10 beer" sig dsapub)
       => nil

Note that you cannot use all combinations of pkey and digest algorithm.  Suitable digests can be found in the (ossl-available-digests) list.  Given a digest DGST there must be an entry RSA-DGST in order to use an RSA key for signatures under DGST.  Respectively there must be an entry DSA-DGST for DSA key pairs and all DSA-suitable digests can also be used for EC keys.

In order to persistently store generated keys the openssl API provides a simple interface to the PEM routines.  PEM is the format for key pairs or public keys.

 -- Function: ossl-pem-read-public-key file
     Return a key (the public part) stored in a PEM structure from FILE.

 -- Function: ossl-pem-read-key file &optional password
     Return a key stored in a PEM structure from FILE. If the (private part of the) key is protected with a password provide (optional) PASSWORD.

 -- Function: ossl-pem-write-public-key file pkey
     Write PKEY (the public part) in a PEM structure to FILE.

 -- Function: ossl-pem-write-key file pkey &optional cipher password
     Write PKEY in a PEM structure to FILE. The key itself is protected by (optional) CIPHER with PASSWORD.

     CIPHER can be set to nil and the key will not be encrypted.  PASSWORD is ignored in this case.

 -- Function: ossl-pem-public-key pkey
     Return PKEY as PEM encoded string.

 -- Function: ossl-pem-key pkey &optional cipher password
     Return PKEY as PEM encoded string.   The key itself is protected by (optional) CIPHER with PASSWORD.

     CIPHER can be set to nil and the key will not be encrypted.  PASSWORD is ignored in this case.

     (ossl-pem-write-key "/tmp/mykey.pem" dsaexmpl)

     (let ((stored (ossl-pem-read-key "/tmp/mykey.pem")))
       (ossl-verify 'SHA1 "I owe you a beer" sig stored))
       => t

     (ossl-pem-public-key rsapub)
       => "-----BEGIN PUBLIC KEY-----
     MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEAtGE7JaGUsVIfLUJmzlkR
     qfB7CJjFlYU7tGmD7C1rBiGz0sjTlPwsMjwwCLP6byRvebVeDxGrxbeyZE3sSB4q
     oVbevhbwwBMY5+j/8q+3l7KbqoP9CGG40ZbEC6IOqFn8kOmliPdWUlogI1Gr4b7U
     R4F+TM6m3r3AQoxeqq+rR5kHat2mvBpxm0o8FZ2KW6ZCAkAXoA3NNCXtSUF9zA6A
     u3acUP4eiFAkS4Q6hIuZli4PzxvUugB5/ekyaa5cRzIEqIhh90mkpqjM9qpR15hk
     39qiM/SxXmLlncU534byldSgnoIse3tnia4WRBm2qK3zTr24zaBtTTXfmRJMWQwZ
     ewIBAw==
     -----END PUBLIC KEY-----
     "

     (ossl-pem-public-key rsaexmpl)
       => "-----BEGIN PUBLIC KEY-----
     MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEAtGE7JaGUsVIfLUJmzlkR
     qfB7CJjFlYU7tGmD7C1rBiGz0sjTlPwsMjwwCLP6byRvebVeDxGrxbeyZE3sSB4q
     oVbevhbwwBMY5+j/8q+3l7KbqoP9CGG40ZbEC6IOqFn8kOmliPdWUlogI1Gr4b7U
     R4F+TM6m3r3AQoxeqq+rR5kHat2mvBpxm0o8FZ2KW6ZCAkAXoA3NNCXtSUF9zA6A
     u3acUP4eiFAkS4Q6hIuZli4PzxvUugB5/ekyaa5cRzIEqIhh90mkpqjM9qpR15hk
     39qiM/SxXmLlncU534byldSgnoIse3tnia4WRBm2qK3zTr24zaBtTTXfmRJMWQwZ
     ewIBAw==
     -----END PUBLIC KEY-----
     "

     (ossl-pem-key rsaexmpl 'AES-256-CBC "foobar")
       => "-----BEGIN ENCRYPTED PRIVATE KEY-----
     MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIk4VVj28lKIgCAggA
     MB0GCWCGSAFlAwQBKgQQ0uzDwfFB2m5ZUCt8K1YSvwSCBNCcQuY1S1c7Blsm7QlH
     iHji7xWcigj6U7U6IQ0y/a6+U/ku2/IQc4I/sjsYNj1ZKBjkHxuWqVtGvKD/AB/r
     2XFYkOpg7X2SvpVXuCkHk/B7l8ifQViqwu3k8r+8jHmLuxa21xysrTLOef8LmCkg
     ePaOArDKascJngpkUtMM269owh1ZBUSmKqQqR+jnpXw+dummdlr2tA0t3Bl+899q
     e6L7sZ330XRTnzyQUuZvBpV8bV0AlaI3jlPROu56MKiUiDU0n9lvzYegWGAJjOvS
     qYW3FPY/B7MczXCFQOmg3XWXWJ/2szRQnWvuM5imhwVF4YbPO30H6KG2sRc5u4Bs
     vym71CdlD77+YEw2dQ36bgjLE2v9aFIuuqqRlbNO1wUo1D0JFrN0ivGnliA6tCQ2
     tADHeEqKeXjCk4GM+rZB9d/kx2RqTgqu+JolaO1+8lxWRMT+aLj5EPN8zHOaRhDL
     3farG89PEOUvPRzkn+18laPBJ0o9AvwYC9Bmi072Lq7XtOIH0iELxY2RyQD0PLD2
     cLdt5tkQDfuUhrUJuhh3waMDa7qe9lMGnxsmlapZn5FY1kZ6gBO79BBu9sFmsr1x
     IRDT5PJc2V+BU9fn3Vu6WM3P56x7WfUjycqpvmu7yshW8D/8KRNYbeBIAXuRKCoJ
     dRIp/c+UWSPDVb68NikRvTRvj0vVrGMsesRkl89uL6liGarGghEV9lwNDQB5XN0q
     wZ+4LXP4DZQlBK3g9Bq7rL6F3ZuxGdThjQO6IAve6MltfOIN/x/schcoE41g2y0D
     0hn8vnDkGwWKFE59qOZ7/iQOOKJisF6MjxnkhlcTvG3ev2mrSsJHdiIoN9u8Zm6o
     Xca/k7JEs3rkr5MhrjxpGznq6Z5skEWEFFGD5XlXKYsHlP5YDxVXLED1cQHKcydC
     t2uPsB/Uqj4zl/lBc0/asQ8gyZiR2Fc5DHjUkmQ/5kH4LulvVLJgCC8JRs2AutBu
     DsqfvEj8uhmCuNYlsrOGrrOo/m2HPQ1DeoJcoL+H8KQG1WjPo0bnaV0Xlf6o+IkI
     G42pDGVeNSZDc+gqbXTzHDF0snOVDO7nfJ+kWmTFpHhI8Ht5H6SaaAIcgIQcWZgZ
     vA7e54XQ6HBUARFaPdw8kXyilu3lGunacjfipCdsOaF3WxGgdPmQTfuQOqglYhlc
     v/WPjyV9WgpEaJKM2Vcwb+CiOeteKnqp9ZLdmb4fZJNP2oIlKLtQSHsmdFGEr7BP
     7hxoi/kcD9QpuC9CHn1rZC99ZwWBa+UmK5s+P8LJRfi6a8qhR3gA6H/JGZLFiW0C
     Vrmir9u33UkroKfn+JTuMZV5e+U7YNFSJhX95nbLTIx44ItyztGlE5+lHMPA+N/p
     +OQM2F0kzfSZ2F1M3x3iO4VPzfEC6vNBpOOhhssSoL9ThNYlCQOVa+QemEjmVuEs
     PLluotrTI3Cetk4nanvj4HGb6KjnkU1JdQ6iKSEl2JnYWrmNNoB3N06/h/7DQpiy
     p/tevEVZQxK2cOklJrgcuNtVXe3c/9OVRKDuu3m1GstbQ4pcqijc55kuEtA0tmvn
     S5WFFj4/cVucd2K3IOHCttEfgDwVNDDzG2shBsKT1JDY4aDOBD3xe+ggstVkGmRL
     ocNNE7QyxF/GOgW9iTQr/yOp9A==
     -----END ENCRYPTED PRIVATE KEY-----
     "

Secure Network Layers
---------------------

The SSL/TLS support in this API is definitely not a copy of the underlying libssl functions.  Instead we chose a higher level of access methods because for example SSL_connect does not work standalone, and having an elisp loop to check socket messages is probably not desirable.

Also we chose to actually do what SSL/TLS stands for, namely to establish a transparent security layer on top of an existing network connection.  That is, you can use the usual open-network-stream+ set-process-filter + process-send-string chain and at some point after the establishment of the connection you can coat that connection with a secure layer.  Of course, this is done transparently and your existing process filter or send-string commands will not notice the change.

 -- Function: ossl-ssl-handshake process &optional method ca cert key serverp
     Perform a handshake on the network connection PROCESS.

     Return a ssl-conn object, or nil if the handshake failed. In the latter case, most likely the remote site cannot handle the specified method, requires a client certificate, or cannot handle ssl at all.

     Optional argument METHOD indicates the SSL connection method, it can be one of tls1 which is the default, ssl23, ssl2, or ssl3.

     Optional argument CA indicates a CA certificate. See ossl-ssl-inject-ca.

     Optional arguments CERT and KEY indicate a peer certificate and possibly a separate key file respectively. See ossl-ssl-inject-peer-cert.

     Optional argument SERVERP indicates whether to perform the handshake as a server if non-nil, and as a client otherwise. Note: In case of a handshake as server it is mandatory to provide a valid certificate and a corresponding key.

Currently there are no high level `open-ssl-stream' (and such) functions.  You have to invoke open-network-stream first and after establishing that connection ossl-ssl-handshake should be performed.

Also, be sure to store the returned SSL-CONN object for later reference.

 -- Function: ossl-ssl-finish ssl-conn
     Finish an SSL connection SSL-CONN.

     Note: This may also finish the network connection.

As noted above, not all peers finish the connection after finishing the SS-Layer but it is highly suggested to do so.  Unpredictible results may occur when you keep using that connection.

 -- Function: ossl-ssl-inject-cert ssl_conn cert &optional key
     Add CERT as the local certificate of SSL-CONN. Optional argument KEY specifies a key file or evp-pkey, if CERT does not contain it.

     Both, CERT and KEY may be either a filename pointing to a PEM-encoded certificate and key respectively, or may be an evp-pkey object.

 -- Function: ossl-ssl-inject-ca ssl-conn ca
     Add CA to the pile of certificate authorities of SSL-CONN. Also force a (re)verification of the remote peer certificate against CA.  Return t if the injection was successful, nil otherwise.

     CA may be either a file name pointing to a PEM-encoded CA certificate, or may be a directory containing a valid bunch of CA certificates according to OpenSSL's CA path layout, or may also be an evp-pkey object.

While ossl-ssl-inject-ca may be used even after handshaking with the remote peer, for example to introduce a certificate authority to verify the remote peer's identity with hindsight, the same does not apply to ossl-ssl-inject-peer-cert since local peer verification at the remote site can only take place at handshake time. Regard that function as convenience function.

     ;; open a https connection to addons.mozilla.org
     (setq p (open-network-stream "moz" "moz" "addons.mozilla.org" 443))
       => #<network connection "moz" (443 . "addons.mozilla.org") state:run>

     (setq m (ossl-ssl-handshake p))
       => #<OpenSSL socket layer: TLSv1 on top of
            #<secure network connection "moz"
              (443 . "addons.mozilla.org") state:run>>

     ;; Let's examine p
     p
       => #<secure network connection "moz"
            (443 . "addons.mozilla.org") state:run>

     (ossl-ssl-finish m)
       => #<OpenSSL socket layer: dead>

     ;; Let's examine p again
     p
       => #<network connection "moz"
            (443 . "addons.mozilla.org") state:exit>

Offering a secure listening socket works quite similar as the following example shows.

     ;; build the acceptor function
     (defun my-acceptor (proc)
       (ossl-ssl-handshake proc 'ssl23 nil
        "/path/to/server.cert" "/path/to/server.key" t))
       => my-acceptor

     ;; establish the listening socket
     (open-network-server-stream "listen" "listen"
      "localhost" 4432 'tcp #'my-acceptor)
       => #<network server accepting connections "listen"
            (4432 . "localhost") state:run>

     We connect using the OpenSSL command line interface.

     freundt@hlid:~$ openssl s_client -ssl3 -connect localhost:4432 \
       -CAfile /etc/ssl/CA/cacert.pem
     CONNECTED(00000003)
     depth=1 /C=DE/ST=Berlin/O=hlidskjalf.org/OU=local CA/CN=hlid.hli
     dskjalf.org/emailAddress=freundt@hlidskjalf.org
     verify return:1
     depth=0 /C=DE/ST=Berlin/O=hlidskjalf.org/OU=ldap client freundt/
     CN=hlid.hlidskjalf.org/emailAddress=freundt@hlidskjalf.org
     verify return:1
     ---
     Certificate chain
      0 s:/C=DE/ST=Berlin/O=hlidskjalf.org/OU=ldap client freundt/CN=
     hlid.hlidskjalf.org/emailAddress=freundt@hlidskjalf.org
        i:/C=DE/ST=Berlin/O=hlidskjalf.org/OU=local CA/CN=hlid.hlidsk
     jalf.org/emailAddress=freundt@hlidskjalf.org
     ---
     Server certificate
     -----BEGIN CERTIFICATE-----
     MIIEQzCCAyugAwIBAgIBAzANBgkqhkiG9w0BAQUFADCBjzELMAkGA1UEBhMCREUx
     DzANBgNVBAgMBkJlcmxpbjEXMBUGA1UECgwOaGxpZHNramFsZi5vcmcxETAPBgNV
     BAsMCGxvY2FsIENBMRwwGgYDVQQDDBNobGlkLmhsaWRza2phbGYub3JnMSUwIwYJ
     KoZIhvcNAQkBFhZmcmV1bmR0QGhsaWRza2phbGYub3JnMB4XDTA2MDcxMjIxMDYy
     MloXDTA3MDcxMjIxMDYyMlowgZoxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJs
     aW4xFzAVBgNVBAoMDmhsaWRza2phbGYub3JnMRwwGgYDVQQLDBNsZGFwIGNsaWVu
     dCBmcmV1bmR0MRwwGgYDVQQDDBNobGlkLmhsaWRza2phbGYub3JnMSUwIwYJKoZI
     hvcNAQkBFhZmcmV1bmR0QGhsaWRza2phbGYub3JnMIIBIjANBgkqhkiG9w0BAQEF
     AAOCAQ8AMIIBCgKCAQEA5CYPHlmyemdoAdNsiemskMm33GYBSCOx1KZEWQ1cfgf0
     vVtpwue+/Nw4UbxYvtnS4ES8pWWEx/YeRyrEtbXg9SzXLSsNTrPT35xmysL87kIN
     nm8F4xGdlFQnvHJ4/55ieUVYi5aSlQtMKOON5HWUWmmWIscNnf3KyGy1lX1mEwhW
     xFYQ01npIz9az0zdBBqhV6mMejEul2vgwqL9lQy7khmwDwzoVdyyAz7C6Nj/7E6i
     gaxad9tc8luQJdMw+E6c67Stz68Om7CWfR7IMoqIx/ag7Ycy56dI8Td5LWvZ+JUG
     KMgHcbJ2mJIjQv3fgp7pIG2McPi91DwNLZhwJheshQIDAQABo4GcMIGZMAkGA1Ud
     EwQCMAAwEQYJYIZIAYb4QgEBBAQDAgTwMAsGA1UdDwQEAwIF4DAsBglghkgBhvhC
     AQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFKc+
     UFGPNVlARKv02s3Agq2q/2YfMB8GA1UdIwQYMBaAFAZIDPDiOBUx1RM67GJRPh+k
     y90DMA0GCSqGSIb3DQEBBQUAA4IBAQAAr8FoMuRaP7YVcnsYVR9Vv6wbjuSlO0rk
     /CtYs5V8QGa7TsGBz+0aBCHeDKegwhEkNowcJrqSlHiLYd4o2sXMqwAyszz1CKF6
     9PpZKXlhwZP5A9Hct9R0THV98f2qNCDOTTj9zxObnMIYJW4WLxGpgPTRTiERRdt3
     VP0aC2vEKb9xblfUQNAyBZKh6bkeKJVvd9WmnUwYkwGfH1+alNIhqCkRPjv8sw31
     ivzqspr2z19mtaYUKIN2u+mddJk7oeJIWNWwpaYicPR0rz9GOmVkgxIxMQAmtLnd
     ka47rxgbVZXtINcOZCNIcvX1hntqE5ItABunG3PGnNJwnqEQEfqJ
     -----END CERTIFICATE-----
     ...
     DONE

which is exactly the expected output.

At the moment it is not possible to establish a server socket with an SSL acceptor and connect to this with another network stream from within the same SXEmacs instance.  This is because the SSL handshake is entirely on top of the process system and the event stream does not know about a special treatmeant of handshakes.  In the above scenario both connections would block each other since the handshake is a multiple turn procedure with a fixed order, but the event loop does not know about the correct order, hence both networks streams would exit prematurely because of timeouts.

However, the above examples are actually all you need to acquire secure sockets.  In order to communicate over the socket the usual tools can be used.  See Processes.

     ;; invoke a local postfix and negotiate a TLSv1 session
     (let ((p (open-network-stream "smtps" "smtps" "localhost" 25)))
       (process-send-string p "STARTTLS\r\n")
       (ossl-ssl-handshake p)
       (process-send-string p "EHLO foo.bar.tld\r\n")
       (process-send-string p "QUIT\r\n"))
       => nil

     we look at the resulting process buffer
     TLS becomes active at the third line (reply code 250)
     ---------- Buffer: smtps ----------
     220 fluch.fresse.org ESMTP Postfix
     220 2.0.0 Ready to start TLS
     250-fluch.fresse.org
     250-PIPELINING
     250-SIZE 10240000
     250-VRFY
     250-ETRN
     250-ENHANCEDSTATUSCODES
     250-8BITMIME
     250 DSN
     221 2.0.0 Bye

     Process smtps exited abnormally with code 256
     -!-
     ---------- Buffer: smtps ----------

Once a successful handshake has been done the returned ssl-conn object can be used to determine and tune useful things.  The following two functions affect the interlinkage between the secure socket and the ordinary network socket.

 -- Function: ossl-ssl-proselytise-process ssl-conn
     Convert the underlying process of SSL-CONN into a secure network connection object.

 -- Function: ossl-ssl-unproselytise-process ssl-conn
     Convert the underlying process of SSL-CONN into an ordinary network connection object.

After handshaking the secure layer is transparently linked to the network socket automatically hence ossl-ssl-proselytise-process need not be called explicitly.  That is also why the buffer output in the above example remained readable.  In contrast ossl-ssl-unproselytise-process unleashes the link between secure socket and network stream.  Having access to the raw (encrypted) SSL stream may have advantages when a program is supposed to just forward the stream to somewhere.

     (let ((p (open-network-stream "smtps" "smtps" "localhost" 25)))
       (process-send-string p "STARTTLS\r\n")
       (let ((sock (ossl-ssl-handshake p)))
         (ossl-ssl-unproselytise-process sock)
         (ossl-ssl-write sock "EHLO foo.bar.tld\r\n")
         (ossl-ssl-write sock "QUIT\r\n")))
       => nil

     ---------- Buffer: smtps ----------
     220 fluch.fresse.org ESMTP Postfix
     220 2.0.0 Ready to start TLS
     < < < < < < < < < <
     < raw binary data >
     > > > > > > > > > >

     Process smtps exited abnormally with code 256
     -!-
     ---------- Buffer: smtps ----------

The snipped portion is a bunch of binary data.  Nonetheless, as can be seen in the example sending data via process-send-string will not work after unproselytising.  There are special I/O functions for this case.

 -- Function: ossl-ssl-read ssl-conn string
     Return the cleartext of STRING which is assumed to be a complete block of data sent through SSL-CONN.

 -- Function: ossl-ssl-write ssl-conn string
     Send STRING to the tunnel SSL-CONN.

After all, unlinking the two layers currently only works in one direction.  The ssl-conn object will always know its parent network-stream.

 -- Function: ossl-ssl-parent ssl-conn
     Return the underlying parent layer of SSL-CONN.

In the linked case the converse can be determined by process-type-data, see Processes.

In order to obtain information about the ciphers which protect the tunnel communication we provide a bunch of useful functions.  At the moment ssl-ciphers are automatically negotiated with the remote site during the handshake procedure and cannot be explicitly requested or set by the user.

 -- Function: ossl-ssl-cipher-version ssl-conn
     Return the protocol version of the tunnel SSL-CONN.

 -- Function: ossl-ssl-cipher-name ssl-conn
     Return the name of the current cipher used in the tunnel SSL-CONN.

 -- Function: ossl-ssl-cipher-names ssl-conn
     Return the names of all supported ciphers in the tunnel SSL-CONN.

 -- Function: ossl-ssl-cipher-bits ssl-conn
     Return the number of effective bits of the current cipher in SSL-CONN.

 -- Function: ossl-ssl-cipher-description ssl-conn
     Return a description of the current cipher used in the tunnel SSL-CONN.

Nowadays secure socket layers not only provide security but also authenticity.  While ciphers are the atoms for the former, certificates play the major role for the latter.  However, authenticity is quite optional within the SSL protocol.  That is why we often append the phrase ``if present'' in the documentation strings of the following functions.

 -- Function: ossl-ssl-cert ssl-conn
     Return the local peer's certificate of SSL-CONN if present, nil otherwise.

 -- Function: ossl-ssl-peer-cert ssl-conn
     Return the remote peer's certificate of SSL-CONN if present, nil otherwise.

 -- Function: ossl-ssl-peer-cert-chain ssl-conn
     Return the certificate chain of SSL-CONN as a list of evp-pkey objects.

 -- Function: ossl-ssl-verify-certificate ssl-conn
     Return a verify code of SSL-CONN.

     The result is a cons cell with the numeric verify code in the car and a verbose string in the cdr.

     (let* ((str (open-network-stream "th" "th" "www.thawte.com" 443))
            (sslc (ossl-ssl-handshake str 'ssl3)))
       (ossl-ssl-peer-cert-chain sslc)
       => (#<OpenSSL X509 Certificate iss:/C=ZA/
             O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA
             sub:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary
             Certification Authority; RSA public key, size 1024>
           #<OpenSSL X509 Certificate iss:/C=ZA/ST=Western Cape/
             L=Cape Town/O=Thawte Consulting (Pty) Ltd/OU=Security/
             CN=www.thawte.com sub:/C=ZA/O=Thawte Consulting (Pty) Ltd./
             CN=Thawte SGC CA; RSA public key, size 1024>)

Certificates which stem from one of these functions are usually wrapped in an evp-pkey object.  In contrast to the public-key handling functions above, certificates usually carry a lot more information. Hence evp-pkey objects with certificate data occupy an additional slot to store X509- and ASN1-specific data.  Nonetheless, passing evp-pkey objects without X509/ASN1 data will not do harm.

Again, certificate specific data in an SSL connection are read-only at the moment.  Only the two injection functions, and the handshake function provide a limited form of influence.

 -- Function: ossl-x509-subject cert
     Return the certificate subject of CERT (an evp-pkey object).

     This will return a string in LDAP syntax.

 -- Function: ossl-x509-issuer cert
     Return the certificate issuer of CERT (an evp-pkey object), that is the organisation which signed the certificate.

     This will return a string in LDAP syntax.

 -- Function: ossl-x509-serial cert
     Return the certificate serial of CERT (an evp-pkey object).

 -- Function: ossl-x509-not-before cert
     Return the certificate valid-not-before time of CERT.

 -- Function: ossl-x509-not-after cert
     Return the certificate valid-not-after time of CERT.

 -- Function: ossl-x509-signature-type cert
     Return the signature type of CERT.

     (let ((p (open-network-stream "go" "go" "gna.org" 443))
           (s (ossl-ssl-handshake p 'tls1)))
       (setq c (ossl-ssl-peer-cert s)))
       => #<OpenSSL X509 Certificate iss:/ST=The Internet/
            O=The OpenSSL Project/CN=www.openssl.org/
            emailAddress=openssl-team@openssl.org
            sub:/ST=The Internet/O=The OpenSSL Project/
            OU=Certificate Authority/CN=OpenSSL CA/
            emailAddress=openssl-team@openssl.org;
            RSA public key, size 1024>

     (ossl-x509-subject c)
       => "/ST=The Internet/O=The OpenSSL Project/CN=www.openssl.org/
           emailAddress=openssl-team@openssl.org"

     (ossl-x509-issuer c)
       => "/ST=The Internet/O=The OpenSSL Project/
           OU=Certificate Authority/CN=OpenSSL CA/
           emailAddress=openssl-team@openssl.org"

     (ossl-x509-serial c)
       => 1

     (ossl-x509-not-before c)
       => "020802062727Z"

     (ossl-x509-not-after c)
       => "030802062727Z"

     (ossl-x509-signature-type c)
       => none