Fox's Pages
UW home Fox's home Updated: August 13, 2007  

EZ SSL - easy to use SSL library

EZ-SSL

EZ-SSL library

Summary: Provides an easy-to-use, SSL protected connection library.
Version: 1.3.1
Download: UNIX: ezs-1.3.1.tar.gz
Windows: ezs-1.3.1.zip

UNIX notes

You should be able to install with

$ ./configure
$ make
$ make install

Programs using the ezs api must include

#include "ezs.h"

and link with at least these libraries (On linux at least you must specify the pthread library)

-lezs -lssl -lcrypto -lpthread

I have had success on AIX with CC=cc_r.

Windows notes

The windows distribution contains libraries compiled for threaded debug and non-debug applications (MD, MDd). It is built for version 0.9.8b of OpenSSL for Windows.

Programs using the windows api must include

#include "ezs.h"

and link with one the correct library: ezsMD.lib or ezsMDd.lib.

Library API

The library provides procedure APIs that allow clients and servers to connect, authenticate, and communicate.

Handling errors

When a procedure returns an error indication, you may retrieve the error code or error code and a text description. Individual error codes are maintained for each connection (EZS). A global error code is maintained for 'unconnected' functions.

Local system errors are indicated by a negative error code: the negative of the system's errno.

If all else fails, setting the external variable ezs_debug to one causes the library to print debug output to stderr.

 ezs_errno

Description: Get the last error code
Syntax:   int ezs_errno(EZS* E)
Arguments:
E: An EZS structure
Return: the error code
Errors:
Notes:
  1. If E is NULL the global error is returned.

 ezs_geterror

Description: Get the last error code and message
Syntax:   int *ezs_geterror(EZS* Echar** txt)
Arguments:
E: An EZS structure
txt:Pointer to a character pointer to receive the error text.
Return: the error code
Errors:
Notes:
  1. If E is NULL the global error is returned.
  2. txt may be NULL. In which case no text is returned.
  3. Caller is responsible for freeing the error text.
  4. The error text includes a description of any SSL errors.
  5. SSL errors are cleared as they are retrieved, so this call may be made only once per error.

Initializing the library

SSL requires initialization for its thread mutexes and its random number seed. You should provide a read/write file for the random seed. You can rewrite this file before exiting to update its 'randomness'.

Usual sequence is

ezs_init(name);
ezs_load_rand_file(filename);

at the start of your program, and

ezs_save_rand_file(filename);

at the end.

 ezs_init

Description: Initializes the openssl and ezs libraries.
Syntax:   void *ezs_init(char* name)
Arguments:
name:session name, may be NULL
Return: 1 (always success)
Errors:
Notes:
  1. This must be called prior to any thread or openssl calls by the program.
  2. The first call to ezs_new will invoke ezs_init with a NULL name if the caller has not already called ezs_init.
  3. Servers can use name to identify SSL sessions. A NULL value deactivates sessions.
  4. The argument is ignored for clients.

 ezs_load_rand_file

Description: Adds data from the file to SSL's 'randomness'.
Syntax:   void *ezs_load_rand_file(char* filename)
Arguments:
filename:read/write file of random data
Return: 1
Errors:
Notes:
  1. This should be called prior to any SSL operation.
  2. You may call this more than once, with different filenames.
  3. This is implemented as a macro

 ezs_save_rand_file

Description: Writes random data to the file.
Syntax:   void *ezs_save_rand_file(char* filename)
Arguments:
filename:read/write file of random data
Return: 1
Errors:
Notes:
  1. This should be called just before your program exits to update the random seed file, but it can be called any time.
  2. This is implemented as a macro

 ezs_new

Description: Allocates a new ezs struct, which will identify a particular connection.
Syntax:   EZS *ezs_new()
Arguments:
Return: Pointer to an EZS structure
Errors:
Notes:
  1. Initializes the library if ezs_init has not been called.

 ezs_free

Description: Frees an ezs struct.
Syntax:   void *ezs_free(EZS* E)
Arguments:
E: An EZS structure
Return:
Errors:
Notes:

Authenticating with certificates

There are several credentials involved in a secure ezs connection:

The authenticating certificate and key consists of a single certificate and key pair that you are using to authenticate to your peer on the connection. Your certificate must have been signed by a CA who is trusted by the peer. Use ezs_add_cert to add certificates and keys to the list of certificates you can use to authenticate to a peer.

Generally if a key file is not specified the key is assumed to be in the same file as the certificate.

A CA certificate is the public certificate of a Certificate Authority (CA) whose signatures you will accept. Use ezs_add_ca to add certificates to the list of acceptable CAs.
A Certificate Revocation List (CRL) identifies certificates that have been revoked by their CA and are no longer valid. Use ezs_add_ca or ezs_add_crl to add to your list of CRLs.

 ezs_add_ca

Description: Add a CA to your list of acceptable CAs.
Syntax:   int ezs_add_ca(char* ca_file)
Arguments:
ca_file: Contains CA certificate to add
Return: 1 = success, 0 = error
Errors:
EZS_ERR_SSL: invalid CA file
Notes:
  1. The certificate will be added to the list of acceptable signers of peer certificates.
  2. Must be PEM format
  3. If the file also contains a CRL entry it will automatically be added to the CRL list.
  4. If the file contains multiple certificates only the first is loaded.

 ezs_add_crl

Description: Add a CRL to your list of CRLs.
Syntax:   int ezs_add_crl(char* crl_file)
Arguments:
crl_file: Contains the CRL to add
Return: 1 = success, 0 = error
Errors:
EZS_ERR_SSL: invalid CRL file
Notes:
  1. The CRL will be added to the list of CRLs used to verify certificates.
  2. Must be PEM format
  3. You can use ezs_add_crl at any time to load a new CRL or to refresh an existing CRL. Changes will take effect on the next connect or accept. Existing connections will be unaffected.
  4. If the file contains multiple CRLs only the first is loaded.

 ezs_add_cert

Description: Adds a certificate to the list of available certificates for authentication to peers.
Syntax:   int ezs_add_cert(char* crtchar* keychar** cn)
Arguments:
crt: Certificate file (PEM)
key: Key file (PEM)
cn: If specified, returns the cert's cn.
Return: 1 = success, 0 = error
Errors:
EZS_ERR_SSL: invalid cert or key
Notes:
  1. If key is NULL the certificate's key will be looked for in the crt file.

 ezs_set_verify

Description: Sets the option to verify the server hostname vs. common name.
Syntax:   void ezs_set_verify(EZS* E)
Arguments:
E: An EZS structure
Return:
Errors:
Notes:
  1. Normally a client or server may choose one of many certificates for any given connection. This option requires that the CN of the certificate match the hostname.
  2. This is implemented as a macro

 ezs_get_peer_cn

Description: Returns the CN of the connected peer's certificate
Syntax:   char ezs_get_peer_cn(EZS* E)
Arguments:
E: An EZS structure
Return:
Errors:
Notes:
  1. The returned value is a pointer into the EZS structure. DO NOT free it.
  2. This is implemented as a macro

Caching sessions

SSL allows the connecting peer to record a session identifier, essentually the private key used for encryption of data, and use that saved identifier to facilitate subsequent sessions. This bypasses the comparitively long public-key negotiation typical of SSL. Session identifier lifetime is normally five minutes.

Server sessions are activated by a non-NULL session name given to ezs_init or by the ezs_set_session_name function. A NULL session name disables sessions for subsequent accpeted connections.

Sessions on the server each consume about 12K bytes of memory, and can accumulate rapidly on busy servers. The expire after about five minutes and are automatically purged.

Clients can save and reuse a session -- thereby saving negotiating overhead on subsequent connections.

 ezs_set_session_name

Description: Set the server's session name or deactivate sessions
Syntax:   void ezs_set_session_name(char* name)
Arguments:
name: name to use for subsequent accepted connections
Return:
Errors:
Notes:
  1. If name is NULL sessions are deactivated.

 ezs_get_session

Description: Retrieve the session identifier associated with the current connection
Syntax:   char *ezs_get_session(EZS* E)
Arguments:
E: An EZS structure
Return: A session identifier
Errors:
EZS_ERR_NO_CONNECTION: No session exists
Notes:
  1. The session identifier is a null-terminated character string.
  2. ezs_get_session must be called after the connection is opened.

 ezs_set_session

Description: Specifies a session identifier to be used for the next connection.
Syntax:   int *ezs_set_session(EZS* Echar* session)
Arguments:
E: An EZS structure
session: A session identifier.
Return: 1 = success, 0 = error
Errors:
EZS_ERR_SSL: invalid session
Notes:
  1. The session identifier is a null-terminated character string.

Handling connections

Connections are not symmetric. Servers generally wait for connection requests from clients. The library provides connection tools to perform both the IP connect, or accept, and the SSL negotiation or just the negotiation step.

If you want to use non-blocking IO, set the non-blocking flag and timeout parameter prior to connecting to a server or accepting client connections. In non-blocking mode any operation may return an EZS_ERR_WOULDBLOCK error. The blocked operation may be retried.

 ezs_use_blocking_io

Description: Sets blocking IO for this connection
Syntax:   int *ezs_use_nonblocking_io(EZS* E)
Arguments:
E: An EZS structure
Return:
Errors:
Notes:
  1. This is the default mode
  2. This is implemented as a macro

 ezs_use_nonblocking_io

Description: Sets non-blocking IO for this connection
Syntax:   int *ezs_use_nonblocking_io(EZS* E)
Arguments:
E: An EZS structure
Return:
Errors:
Notes:
  1. This is implemented as a macro

 ezs_set_timeout

Description: Sets semi-blocking timeout for this connection
Syntax:   int *ezs_set_timeout(EZS* Eint timeout
Arguments:
E: An EZS structure
timeout: seconds to block before reporting a wouldblock error.
Return:
Errors:
Notes:
  1. This is implemented as a macro

 ezs_allow_v2

Description: Allow connections using v2 protocol
Syntax:   void ezs_allow_v2(EZS* E)
Arguments:
E: An EZS structure
Return:
Errors:
Notes:
  1. By default the library uses only SSLv3.
  2. This is implemented as a macro

 ezs_connect

Description: Client connect and authenticate to a server
Syntax:   int ezs_connect(EZS* Echar* servicechar* cn)
Arguments:
E: An EZS structure
service: The service address: (host:port).
cn: CN of the certificate to use for authentication
Return: 1 = success, 0 = error
Errors:
EZS_ERR_NOCRT: cert (cn) not loaded
EZS_ERR_BADCRT: cert invalid
EZS_ERR_BADKEY: key invalid
EZS_ERR_SSL: connection failure
Notes:
  1. The 'cn' must have been previously loaded with ezs_add_cert.
  2. On success the connection will be open and verified.
  3. You can find the peer's cn with ezs_get_peer_cn.
  4. If you choose not to retry a WOULDBLOCK error you must call ezs_disconnect to close the partially open connection.

 ezs_connect_fd

Description: Client authenticate to a server over already connected socket
Syntax:   int ezs_connect_fd(EZS* Eint fdchar* cn)
Arguments:
E: An EZS structure
fd: The connected file descriptor
cn: CN of the certificate to use for authentication
Return: 1 = success, 0 = error
Errors:
EZS_ERR_NOCRT: cert (cn) not loaded
EZS_ERR_BADCRT: cert invalid
EZS_ERR_BADKEY: key invalid
EZS_ERR_SSL: connection failure
Notes:
  1. You can find the peer's cn with ezs_get_peer_cn.
  2. If you choose not to retry a WOULDBLOCK error you must call ezs_disconnect to close the partially open connection.

 ezs_listen

Description: Listen on a port for connection requests.
Syntax:   int ezs_listen(EZS* Echar* port)
Arguments:
E: An EZS structure
port: Port to listen on
Return: 1 = success, 0 = error
Errors:
EZS_ERR_SSL: listen failure
Notes:
  1. Note that the port is a character string

 ezs_accept

Description: Server accept connection and verify client
Syntax:   EZS *ezs_accept(EZS* Echar* cn)
Arguments:
E: An EZS structure
cn: CN of the certificate to use for authentication
Return: An EZS structure, NULL = error
Errors:
EZS_ERR_NOCRT: cert (cn) not loaded
EZS_ERR_BADCRT: cert invalid
EZS_ERR_BADKEY: key invalid
EZS_ERR_SSL: accept failure
Notes:
  1. On success the connection will be open and verified.
  2. You can find the peer's cn with ezs_get_peer_cn.
  3. If you choose not to retry a WOULDBLOCK error you must call ezs_disconnect to close the partially open connection.

 ezs_accept_fd

Description: Server verify connected client
Syntax:   int ezs_accept_fd(EZS* Eint fdchar* cn)
Arguments:
E: An EZS structure
fd: The connected file descriptor
cn: CN of the certificate to use for authentication
Return: 1 = success, 0 = error
Errors:
EZS_ERR_NOCRT: cert (cn) not loaded
EZS_ERR_BADCRT: cert invalid
EZS_ERR_BADKEY: key invalid
EZS_ERR_SSL: accept failure
Notes:
  1. On success the connection will be open and verified.
  2. You can find the peer's cn with ezs_get_peer_cn.
  3. If you choose not to retry a WOULDBLOCK error you must call ezs_disconnect to close the partially open connection.

 ezs_disconnect

Description: Disconnect a connection
Syntax:   void ezs_disconnect(EZS* E)
Arguments:
E: An EZS structure
Return:
Errors:
Notes:

Reading and writing

Blocking reads and writes are 'semi-blocking'. The library will wait timeout seconds for the operation to complete. Non-blocking reads and writes will return immediately.

Regardless of the blocking mode, a read or write may return one of the 'wouldblock' errors.

Because an SSL connection can be renegotiated either of these conditions may be returned for either a read or a write. You can use the ezs_wouldblock procedure to see if the IO operation failed for either reason.

Because the SSL library buffers incoming data, use the ezs_bytes_for_read procedure in addition to a select to see if there are bytes available for read.

 ezs_read

Description: Read bytes from a peer len bytes.
Syntax:   int ezs_read(EZS* Echar* dataint len)
Arguments:
E: An EZS structure
data: Memory location to receive data. Must be at least
len: maximum number of bytes to read
Return: number of bytes read, 0 = error
Errors:
EZS_ERR_NO_CONNECTION: no connection
EZS_ERR_READ_WOULDBLOCK: read blocked (waiting for read)
EZS_ERR_WRITE_WOULDBLOCK: read blocked (waiting for write)
EZS_ERR_READ_EOF: connection closed by peer
EZS_ERR_SSL: SSL error
Notes:
  1. If you choose to retry a blocked read you must supply the exact same data with the retry'd ezs_read. You may supply a copy of the original data however.
  2. If you choose not to retry a blocked read you must call ezs_disconnect to close the open connection.
  3. You should call ezs_disconnect to close a connection after receiving an end-of-file.

 ezs_write

Description: Write bytes to a peer
Syntax:   int ezs_write(EZS* Echar* dataint len)
Arguments:
E: An EZS structure
data: Pointer to data.
len: number of bytes to write
Return: number of bytes written, 0 = error
Errors:
EZS_ERR_NO_CONNECTION: no connection
EZS_ERR_READ_WOULDBLOCK: write blocked (waiting for read)
EZS_ERR_WRITE_WOULDBLOCK: write blocked (waiting for write)
EZS_ERR_READ_EOF: connection closed by peer
EZS_ERR_SSL: SSL error
Notes:
  1. Success means all the data were written
  2. If you choose to retry a blocked write you must supply the exact same data with the retry'd ezs_write. You may supply a copy of the original data however.
  3. If you choose not to retry a blocked write you must call ezs_disconnect to close the open connection.
  4. You should call ezs_disconnect to close a connection after receiving an end-of-file.

 ezs_bytes_for_read

Description: Get number of bytes available for read
Syntax:   int ezs_bytes_for_read(EZS* E
Arguments:
E: An EZS structure
Return: number of bytes buffered and available for a read operation
Errors:
Notes:

 ezs_wouldblock

Description: Test if the last IO attempt would have blocked
Syntax:   int ezs_wouldblock(EZS* E
Arguments:
E: An EZS structure
Return: 1 if the last IO operation returned EZS_ERR_READ_WOULDBLOCK or EZS_ERR_WRITE_WOULDBLOCK, 0 otherwise
Errors:
Notes:

Encrypting data

Some convenience routines are provided to encrypt and decrypt data and to compute MAC hashes. Encryption generally includes an initialization vector (IV) that ensures randomness in the start of a message. The same, random IV must be used for both encryption and decryption.

Keys are any binary strings, and should be at least 32 bytes long.

The default encryption cipher is 128 bit AES in CBC mode.

The default hash algorithm is SHA-1.

Use ezs_set_cipher and ezs_set_hash to select other algorithms.

 ezs_crypt_init

Description: Initialize cryption structures
Syntax:   int ezs_crypt_init(EZS* Echar* keyint lenchar** iv)
Arguments:
E: An EZS structure
key: Encryption key
len: length of the key
iv: initialization vector
Return: 1
Errors:
Notes:
  1. If iv is NULL an IV will be computed, used and discarded.
    If iv points to a NULL pointer an IV will be computed, used and a pointer to it returned in iv. If iv points to a non-NULL pointer it is assumed to be a predefined IV and will be used.
  2. The IV used in for encryption must be provided to the decryption step or the first block of decrypted data will be lost.

 ezs_crypt

Description: Encrypt or decrypt data
Syntax:   int ezs_crypt(EZS* Eint modechar* outint out_lenchar* inint in_len)
Arguments:
E: An EZS structure
mode: Either EZS_ENCRYPT or EZS_DECRYPT
out: buffer to receive the crypted text
out_len: length of data stored in the buffer
in: data to encrypt or decrypt
in_len: length of data
Return: 1 = success, 0 = error
Errors:
EZS_ERR_SSL: cryption failure
Notes:
  1. Because of the possibility of block padding, the output buffer's length should be at least in_len + EZS_BLOCKSIZE(E)
  2. You must call ezs_crypt_init prior to ezs_crypt.

 ezs_hmac

Description: Generate a MAC of some data
Syntax:   int ezs_hmac(char** macint* mac_lenchar* dataint data_lenchar* keyint key_len)
Arguments:
data: data for which the MAC will be computed
data_len: length of data
key: key for the MAC
key_len: length of the key
mac: computed MAC
mac_len: length of mac
Return: 1
Errors:
Notes:

 ezs_data_to_base64

Description: Convert binary data to printable base64
Syntax:  int ezs_data_to_base64(char** b64int* b64_lenchar* dataint data_len)
Arguments:
data: input binary data
data_len: length of data
b64: base64 representation of data
b64_len: length of b64 text
Return: 1
Errors:
Notes:

 ezs_base64_to_data

Description: Convert base64 to binary data
Syntax:  int ezs_base64_to_data(char** dataint* data_lenchar* b64int b64_len)
Arguments:
b64: input base64 representation of data
b64_len: length of b64 text
data: output binary data
data_len: length of data
Return: 1
Errors:
Notes:

 ezs_set_cipher

Description: Sets the cipher used by the encryption procedures
Syntax:  int ezs_set_cipher(EVP_CIPHER* (*cipher)())
Arguments:
cipher: any openssl cipher algorithm
Return:
Errors:
Notes:
  1. Ciphers supported by your version of the openssl library may be found in openssl's inlcude file evp.h.
  2. The default cipher is EVP_aes_128_cbc (128 bit AES in CBC mode)

 ezs_set_hash

Description: Sets the hash algorithm used by the encryption and hash procedures
Syntax:  int ezs_set_hash(EVP_MD* (*hash)())
Arguments:
cipher: any openssl hash algorithm
Return:
Errors:
Notes:
  1. Hashes supported by your version of the openssl library may be found in openssl's inlcude file evp.h.
  2. The default is EVP_sha1 (SHA-1)

Version information

The EZS library will report both its own version and the versions of openssl it was compiled and linked with. The latter can help detect cases where the library is linked with different and incompatible versions of openssl.

 ezs_version

Description: Returns ezs and openssl version information.
Syntax:   char *ezs_version()
Arguments:
Return: Version information as a string.
Errors:
Notes:
  1. Caller is responsible for freeing the returned version string.

License

 * =================================================================
 * Copyright (c) 2006 The University of Washington
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =================================================================
Examples

 Client

Basic client connect and read loop


 #include "ezs.h"

 static char *hw = "Hello, world.";

main()
{
   EZS *E;
   char *mycn;
   int r;
   char resp[4096];

   /* allocate an ezs struct */
   E = ezs_new(NULL);

   /* add the CA info */
   if (!ezs_add_ca(CA's certificate file)) {
      fprintf(stderr, "set ca: %d\n", ezs_errno(E));
      exit (1);
   }

   /* add our cert */
   if (!ezs_add_cert(my cert and key file, NULL, &mycn)) {
      fprintf(stderr, "set cert: %d\n", ezs_errno(E));
      exit (1);
   }

   /* connect to server */
   if (!ezs_connect(E, server host:port, mycn)) {
      fprintf(stderr,">> Connect error, err = %d\n",
              ezs_errno(E));
      exit (1);
   }

   /* send message */

   if ((r=ezs_write(E, hw, strlen(hw)<0) {
       fprintf(stderr, ">> write failed, %d\n",
              ezs_errno(E));
       exit (1);
   }

   /* read reply */

   if ((r=ezs_read(E, resp, 4095))<=0) {
      fprintf(stderr, ">> read failed, %d\n",
              ezs_errno(E));
      exit (1);
   }

   resp[r] = '\0';
   printf("Received: %s\n", resp);

   ezs_disconnect(E);

}


 Crypto

Encrypt and compute a MAC


 #include "ezs.h"

 static char *hw = "Hello, world.";
 static key = "my key string";

 #define UC (unsigned char*)

main()
{
   EZS *E;
   unsigned char *e_hw;
   int e_len;
   unsigned char *mac;
   int mac_len;
   unsigned char *iv = NULL;
   char *mac64, *e64;

   /* alloc an ezs struct */
   ezs_init(NULL);
   E = ezs_new(NULL);
   
   /* Initialize */
   ezs_crypt_init(E, key, strlen(key), &iv);

   /* encrypt */
   e_hw = UC malloc(strlen(hw)+EZS_BLOCKSIZE(E));
   
   ezs_crypt(E, EZS_ENCRYPT, e_hw, &e_len, UC hw, strlen(hw));

   /* get mac */
   ezs_hmac(&mac, &mac_len, e_hw, e_len, UC key, strlen(key));

   ezs_data_to_base64(&mac64, NULL, (void*)mac, mac_len);
   ezs_data_to_base64(&e64, NULL, (void*)e_hw, e_len);

   printf("%s encoded = %s\n", hw, e64);
   printf("mac = %s\n", mac64);
}
  

Procedures

Examples


Jim Fox
UW Technology
Identity and Access Management
University of Washington
fox@washington.edu

© 1983-2009, University of Washington