





                                   RIPEM API
                               Library Reference

                             for RIPEM version 3.0

                                 Jeff Thompson

                              last modified 5/7/96







     NOTICE: No representations are made concerning either the
     merchantability of this software or the suitability of this software
     for any particular purpose. It is provided "as is" without express or
     implied warranty of any kind.

     License to copy and use this software is granted provided that these
     notices are retained in any copies of any part of this documentation
     and/or software.






                                    CONTENTS



     INTRODUCTION                                               3

      Example Application: RSIGN                                4
       Compiling the RIPEM library                              8
       Compiling and running RSIGN                              8


     RIPEM API FUNCTION OVERVIEW                               10

      Lists: The TypList Structure                             10

      RIPEM Database                                           10

      RIPEM Database Cursor                                    11

      Distinguished Names                                      11

      Attributes: The RIPEMAttributes Structure                13

      Decoding Certificates                                    13

      Logging In and Managing User Keys                        14

      Managing Certification Information                       14

      Message Processing                                       15

      Utility Functions                                        16

      Compatibility with Early Versions of RIPEM               16


     RIPEM API FUNCTION DETAILS                                18


     APPENDIX A: SELECTED RSAREF FUNCTIONS                     52

      Random Structures                                        52

      R_DigestBlock                                            53











                                       2






                               INTRODUCTION

     The RIPEM library provides an application programming interface (API)
     which an application can call to perform privacy-enhanced messaging.
     The RIPEM API is carefully designed so that the RIPEM library performs
     the cryptography and message formatting and parsing while leaving all
     of the input/output operations up to the application. Since it is the
     application, and not the RIPEM library, which performs the message I/O
     and user interfacing, the RIPEM library can be used in many
     environments. One example is the command-line application (provided
     with the standard RIPEM release) which processes messages in files and
     takes most input from command-line arguments. But the RIPEM library
     can also be used, for example, in an application where message I/O is
     done with memory buffers or communications ports, and user input is
     done with a graphical user interface.

     Throughout this document, "you" refers to you, the programmer, who are
     writing an application which makes calls to the RIPEM API. It is
     assumed you have read the RIPEM User's Guide and are therefore
     familiar with the following topics:

     * the basics of public key encryption: what public and private keys
       are

     * cryptographic "signatures" made with the private key

     * certificates which are used to validate a user's public key

     * certificate chains and certificate statuses (VALID, PENDING,
       EXPIRED, etc.)

     * setting a user's "chain length allowed" to allow extended
       certificate chains

     * certificate revocation lists (CRLs) which are used as a "hot list"
       for revoked private keys

     * the difference between MIC-ONLY, MIC-CLEAR and ENCRYPTED messages

     * the difference between ripem1 and pem format messages

     * the RIPEM home directory

     * the RIPEM preferences file which holds important information like
       chain length allowed

     It is also assumed you know how to use the random number functions and
     R_DigestBlock from RSAREF. Full details are in rsaref.txt in the
     RSAREF doc directory. The relevant details are reproduced for your
     convenience in Appendix A.

     Questions should be sent to the Usenet newsgroup alt.security.ripem.



                                       3


                   RIPEM API LIBRARY REFERENCE: Introduction


     The rest of this chapter introduces RIPEM with an example application.
     The chapter "RIPEM API Function Overview" gives a quick overview of
     the RIPEM API functions in their natural groupings. And, the chapter
     "RIPEM API Function Details" lists the API functions alphabetically
     and gives details on calling requirements. Also, some relevant
     excerpts from the RSAREF reference manual are reproduced in Appendix
     A.

     Of course, another great source of "documentation by example" is the
     command-line applications RIPEM and RCERTS provided with the standard
     RIPEM release. To see any of the RIPEM API functions used in context,
     you can scan the files in the ripem/cmdline directory. Also, the full
     source of the RIPEM library is in the ripem/main directory, and
     looking at how an API functions is coded may answer many of your
     questions.

     Example Application: RSIGN

     As an introduction to the RIPEM API, here is a short application,
     called RSIGN, which takes an input line of text and creates a MIC-
     CLEAR message from it. RSIGN is meant to show the minimum set of calls
     needed to use the RIPEM API. To keep the code simple, the output
     message is sent to stdout and the RIPEM home directory, username and
     password are hard-coded. This code is provided in the file rsign.c in
     the RIPEM documentation directory.

     #include <stdio.h>
     #include <string.h>
     #include "global.h"
     #include "rsaref.h"
     #include "ripem.h"
           All RIPEM applications must include stdio.h, global.h, rsaref.h,
           and ripem.h. stdio.h is needed because ripem.h uses the FILE
           type for database I/O, etc. global.h is used by rsaref.h and
           ripem.h. rsaref.h is the header file for the RSAREF
           cryptographic library which defines types like R_PUBLIC_KEY. And
           ripem.h is the header file for the RIPEM API.

     void main ()
     {
       RIPEMInfo ripemInfo;
           The ripemInfo structure holds the information used by RIPEM such
           as the user's name, public and private keys, and certification
           preferences. The ripemInfo structure also holds internal state
           information used between calls such as RIPEMEncipherUpdate and
           RIPEMEncipherFinal. This is because the RIPEM API library is
           fully reentrant, meaning that all dynamic memory is maintained
           by the caller of the library, as opposed to a global variable
           within the library's data space.

       RIPEMDatabase ripemDatabase;
           The RIPEMDatabase structure holds information and FILE handles
           for the RIPEM data files such as pubkeys, privkey, crls and
           preferen.


                                       4


                   RIPEM API LIBRARY REFERENCE: Introduction


       char *errorMessage;
       unsigned char *partOut, line[256];
       unsigned int partOutLen, lineLen;

       RIPEMInfoConstructor (&ripemInfo);
           This initializes ripemInfo, setting memory pointers to NULL,
           etc. RIPEMInfoConstructor must be called for any RIPEMInfo
           structure before it is used. Also, RIPEMInfoDestructor must be
           called when it is done being used to free up any memory, etc. It
           is fair to say that the RIPEM API is a C++ "wannabe". If the
           RIPEM API were actually C++, RIPEMInfo would be a class and the
           constructor and destructor would automatically be called.

       RIPEMDatabaseConstructor (&ripemDatabase);
           Similar to RIPEMInfoConstructor, RIPEMDatabaseConstructor must
           be called for any RIPEMDatabase structure before it is used.
           Also, RIPEMDatabaseDestructor must be called when it is done
           being used to close files, etc.

       /* For error, break to end of do while (0) block. */
           Processing must be aborted if any RIPEM API function returns an
           error (except for some explicitly stated cases such as
           RIPEMLoginUser returning ERR_PREFERENCES_CORRUPT). The do {}
           while (0) construct simply sets up a block where any break
           statement (usually due to error) will cause a jump to the end of
           the block. This is a more structured approach than using a goto
           statement.

       do {
         /* Initialize the database to the home directory, which must
     already
              exist and end in the directory seperator.
          */
         if ((errorMessage = InitRIPEMDatabase
              (&ripemDatabase, ".\\", &ripemInfo)) != (char *)NULL)
           break;
           InitRIPEMDatabase opens all the data files in the RIPEM home
           directory, or creates them if they don't exist. For simplicity
           here, the directory name is hard-coded. A full implementation
           would get this from an environment variable, a command line
           argument, etc.

           To get a full path name, RIPEM simply concatentates the file
           name to the supplied RIPEM home directory. Therefore, the
           correct seperator for the operating system must be at the end of
           the directory string as supplied to InitRIPEMDatabase. (Here it
           is the DOS backslash, but it could be the UNIX forward slash,
           etc.) Also, even though RIPEM can create the data files, it
           cannot create the RIPEM home directory itself. Therefore, the
           calling application is responsible for checking that the
           directory exists and for creating it if it doesn't. The
           documentation for InitRIPEMDatabase suggests a way to check if
           the directory exists.



                                       5


                   RIPEM API LIBRARY REFERENCE: Introduction


           As with most RIPEM API functions, this returns (char *)NULL for
           success or a pointer to an error string. The if statement here
           simply calls the function, assigns errorMessage to the return
           value and breaks if it is not null.


         if ((errorMessage = RIPEMLoginUser
              (&ripemInfo, "test", &ripemDatabase,
               (unsigned char *)"password", strlen ("password")))
             != (char *)NULL)
           break;
           This operation unlocks the user's private key using the supplied
           password and places the user's private key, public key, self-
           signed certificate and other information into ripemInfo.

           For simplicity here, the username and password are hard-coded. A
           full implementation would typically get the username from an
           environment variable and get the password by prompting the user.


         /* Prepare for a MIC-CLEAR message. Use null values for
              encryptionAlgorithm, recipientKeys and recipientKeyCount.
          */
         if ((errorMessage = RIPEMEncipherInit
              (&ripemInfo, MODE_MIC_CLEAR, MESSAGE_FORMAT_RIPEM1, 0,
               (RecipientKeyInfo *)NULL, 0)) != (char *)NULL)
           break;
           The RIPEM API uses an Init/Update/Final structure to process
           data, similar to RSAREF. Here, the operation to create a MIC-
           CLEAR message is initialized. Note that a pointer to the
           ripemInfo is passed in so that RIPEM can use it to keep state
           information between init, update and final.


         /* Read in the test string from the stdin. Using fgets instead of
              gets means we get the ending '\n' as required by
     RIPEMEncipher.
          */
         puts ("Enter text to sign (one line):");
         fgets ((char *)line, sizeof (line), stdin);
         lineLen = strlen ((char *)line);
           The documentation for RIPEMEncipherUpdate describes what format
           is required for the text to be processed. Specifically, using
           fgets instead of gets keeps the '\n' end of line character
           required by RIPEM.


         /* Digest the message.  (In a file-based application, this would
              be called multiple times as each part of the file is read
     in.)
          */
         if ((errorMessage = RIPEMEncipherDigestUpdate
              (&ripemInfo, line, lineLen)) != (char *)NULL)
           break;


                                       6


                   RIPEM API LIBRARY REFERENCE: Introduction


           There is a "bug" in the standard for PEM messages in that the
           signature information is placed before the text, instead of
           after. This means that an application must output the digital
           signature before it writes the text to the output. Therefore a
           first pass on the text is necessary to simply digest it for
           computing the signature, hence the need for
           RIPEMEncipherDigestUpdate. After this, the signature is written
           to the output stream and RIPEMEncipherUpdate is used to actually
           write out the text, encoding and encrypting it as necessary.
           (For deciphering a message, only one pass is necessary, so there
           is no "RIPEMDecipherDigestUpdate".)

           RIPEM can process text of unlimited length by calling update
           multiple times. Here, we have a short buffer so only one call is
           necessary. But a full implementation would read in part of the
           text, call RIPEMEncipherDigestUpdate, read in the next part,
           etc.


         /* Produce the message output and write to stdout.
            (In a file-based application, the file would first be
            rewound and this function would be called multiple times
            as each part of the file is read in.)
          */
         if ((errorMessage = RIPEMEncipherUpdate
              (&ripemInfo, &partOut, &partOutLen, line, lineLen,
               &ripemDatabase)) != (char *)NULL)
           break;
           RIPEMEncipherUpdate performs the second pass on the text,
           producing the output text encoded and encrypted as necessary.
           Here, we have a short buffer so only one call is necessary. But
           a full implementation would read in part of the text, call
           RIPEMEncipherUpdate, write out the result, read in the next
           part, etc. (The first call to RIPEMEncipherUpdate also outputs
           all the header information including the signature.) When using
           a file, don't forget to rewind it after reading it in for
           RIPEMEncipherDigestUpdate.

           Note that partOut itself is an unsigned char *, and we pass a
           pointer to it so that RIPEMEncipherUpdate can set partOut to a
           working buffer which contains the output. This buffer is
           allocated by RIPEM and maintained within ripemInfo. This way,
           the caller does not need any guesswork to allocate output
           buffers of the right size. But, the buffer pointed to by partOut
           is only valid until the next call to RIPEM, so it must be
           written out or copied immediately. Also, the caller does not
           need to free the memory buffer returned in partOut. This is done
           by RIPEMInfoDestructor.

         fwrite (partOut, 1, partOutLen, stdout);
           Write the output buffer returned by RIPEMEncipherUpdate.


         /* Finalize and flush the final output.


                                       7


                   RIPEM API LIBRARY REFERENCE: Introduction


          */
         if ((errorMessage = RIPEMEncipherFinal
              (&ripemInfo, &partOut, &partOutLen, &ripemDatabase))
             != (char *)NULL)
           break;
         fwrite (partOut, 1, partOutLen, stdout);
           This finalizes the enciphering process, flushing any internal
           buffers and writing out the end message boundary. The output
           buffer is returned by RIPEMEncipherFinal in the same manner as
           with RIPEMEncipherUpdate. RIPEMEncipherFinal should only be
           called once.

       } while (0);

       RIPEMInfoDestructor (&ripemInfo);
       RIPEMDatabaseDestructor (&ripemDatabase);
           These are the matching destructors to RIPEMInfoConstructor and
           RIPEMDatabaseConstructor, zeroizing any sensitive data and
           freeing allocated memory. These should be called regardless of
           any errors during processing.


       if (errorMessage != (char *)0)
         printf ("ERROR: %s\n", errorMessage);
           If we broke out of the do while (0) block because of an error,
           errorMessage is set to the string returned by the function, so
           print it here.

     }

     Compiling the RIPEM library

     All of the source to the RIPEM library is in the ripem/main directory
     of the standard RIPEM release. All .c files should be compiled, except
     for msc7.c which should only be used with Microsoft C version 7.0 and
     above, and except for timeshif.c which should only be used with the
     Macintosh Turbo C compiler.

     For examples of the flags to use when compiling, see the makefile that
     is used to make the RIPEM command-line application for your platform.
     Note that the Unix makefile, for example, compiles all of the RIPEM
     library source in ripem/main as well as the command-line source in
     ripem/cmdline and links all these object files explicitly into the
     executable. However, it is also possible (and preferable) to load the
     object files from ripem/main into an object library first, such as
     ripemlib.a, and then to link the library to the object files for the
     application (such as those in ripem/cmdline).

     Compiling and running RSIGN

     Since the RIPEM home directory, username and password are hard-coded,
     you may either change the code to use your own, or you can simply
     create the test user. To create the test user, change directory to



                                       8


                   RIPEM API LIBRARY REFERENCE: Introduction


     where you will run RSIGN and enter the following at the command
     prompt:

       ripem -g -R eks -H . -u test -k password

     Compile rsign.c and link it to the RIPEM library and the RSAREF
     library. When you run RSIGN, it gives the prompt:

       Enter text to sign (one line):

     Enter a single line of text, such as "This is a signed message." RSIGN
     then prints the MIC-CLEAR message which looks like:

     -----BEGIN PRIVACY-ENHANCED MESSAGE-----
     Proc-Type: 2001,MIC-CLEAR
     Content-Domain: RFC822
     Originator-Name: test
     Originator-Certificate:
      MIIBrzCCAVkCEQCg1MQZAAQ8vfaemcgFv1WjMA0GCSqGSIb3DQEBAgUAMFwxCzAJ
      BgNVBAYTAlVTMSAwHgYDVQQKExdSU0EgRGF0YSBTZWN1cml0eSwgSW5jLjEcMBoG
      A1UECxMTUGVyc29uYSBDZXJ0aWZpY2F0ZTENMAsGA1UEAxMEdGVzdDAeFw05NTAz
      MDcwNTAwMzhaFw05NjAzMDYwNTAwMzhaMFwxCzAJBgNVBAYTAlVTMSAwHgYDVQQK
      ExdSU0EgRGF0YSBTZWN1cml0eSwgSW5jLjEcMBoGA1UECxMTUGVyc29uYSBDZXJ0
      aWZpY2F0ZTENMAsGA1UEAxMEdGVzdDBZMAoGBFUIAQECAgIAA0sAMEgCQQDOdsGA
      xEgbvsKuOZcQSRHvHXP9IrVOzfqP/FKuuamitXvq2iU0+zYXhqYZLivY5elgZZz0
      gaTWzv9CfRV33wD5AgMBAAEwDQYJKoZIhvcNAQECBQADQQA77MfGHXhNHFPEIIAr
      +p9kQ9L3zqvaog02WgtidUNZmYWNSZSHgkE0ARprtHiLyeBcx+qwtH+AKpKG6piR
      0ytq
     MIC-Info: RSA-MD5,RSA,
      zB6IRi4tdvg0ORL31shK7GEzT0Hedc35gLUWKKGcJHDUsa9tJ8YlS58UBQkLQp8r
      W4EROcRzfXdUYzSUUOQmXw==

     This is a signed message.
     -----END PRIVACY-ENHANCED MESSAGE-----






















                                       9





                        RIPEM API FUNCTION OVERVIEW

     This chapter is a quick overview the RIPEM API functions. This is
     meant to give a general sense of how the API functions would fit into
     your application, and describes the approach to using certificates,
     lists and other special constructs. Specific details are given in the
     following chapter, RIPEM API Function Details. Also, you should be
     familiar with the RIPEM concepts explained in the RSIGN example in the
     introduction.

     Lists: The TypList Structure

     The need for a general list handling structure in RIPEM is answered by
     the TypList structure. The most important use of TypList is the list
     of certificates in a certificate chain returned by SelectCertChain.
     TypList is defined as:

     typedef struct struct_list {
       struct struct_list_entry *firstptr;  /* NULL if empty list */
       struct struct_list_entry *lastptr;
     } TypList;

     typedef struct struct_list_entry {
       struct struct_list_entry *nextptr;      /* NULL if no next */
       struct struct_list_entry *prevptr;  /* NULL if no previous */
       void *dataptr;
       unsigned int datalen;
     } TypListEntry;

     When you declare a TypList structure, you must call InitList before
     using it and FreeList when finished. You can add a generic entry to a
     TypList using AddToList or PrependToList, and you can add an entry
     which is a null-terminated C string using AppendLineToList. But
     typically, RIPEM adds the entries to the list and you only need to
     access them.

     To see if a list has any entries at all, check firstptr: if it is
     (TypListEntry *)NULL then the list is empty. To access each entry in
     order, you can use the following code:

     TypList list;
     TypListEntry *entry;

     /* ... a call to a function which fills the list */

     for (entry = list.firstptr; entry; entry = entry->nextptr) {
       /* entry->dataptr and entry->datalen are the current entry
            and its length.  For example, if the list was filled
            by SelectCertChain, the certificate encoding is
            (unsigned char *)entry->dataptr */
     }

     RIPEM Database



                                       10


                 RIPEM API LIBRARY REFERENCE: Function Overview


     Many RIPEM functions require access to the files in the RIPEM database
     which hold public keys, private keys, etc. When you declare a
     RIPEMDatabase structure, you must call RIPEMDatabaseConstructor before
     using it and RIPEMDatabaseDestructor when finished. To initialize the
     database with the files in the RIPEM home directory, use
     InitRIPEMDatabase.

     RIPEM Database Cursor

     You can use the RIPEM database cursor to search for arbitrary
     certificates in the database. When you declare a RIPEMDatabaseCursor
     structure, you must call RIPEMDatabaseCursorConstructor before using
     it and RIPEMDatabaseCursorDestructor when finished. To begin a search,
     use RIPEMCertCursorInit. And to select each successive certificate,
     use RIPEMCertCursorUpdate. The database cursor can be used, for
     example, to select certificates from the database to include in a
     certs-and-CRLs-only message.

     Distinguished Names

     A distinguished name, for example "common name = fred@snark.edu, org
     unit = Persona Certificate, organization = RSA Data Security, Inc.,
     country = US", is used to identify a user as the subject or issuer of
     a certificate. To use distinguished names, you need to know the
     details of how they are constructed.

     A distinguished name has a hierarchy of levels called "relative
     distinguished names" (RDN). In the example above, the RDN at the most
     significant level is "country = US" and the least significant RDN is
     "common name = fred@snark.edu". Within a level, there can be one or
     more "attribute value assertions" (AVA). Typically, each RDN only has
     one AVA, but in the example above it is possible that the least
     significant RDN might have two AVAs such as "common name =
     fred@snark.edu" as well as "locality = Cambridge".

     An attribute value assertion has an attribute type, such as "country",
     and a value, such as "US". Furthermore, the value has a tag which
     tells the character set of the value. RIPEM recognizes the value tags
     for "printable string" and "T.61 string". You can think of printable
     string as a simple subset of ASCII and T.61 as a superset of ASCII
     which you can use as a catchall for values which don't fit printable
     string. Use the function IsPrintableString to distinguish these.

     RIPEM uses the DistinguishedNameStruct to represent distinguished
     names, which is defined as follows:

     typedef struct DistinguishedNameStruct {
       /* Most significant AVAs and RDN are listed first. */
       short AVATypes[MAX_AVA];                    /* -1 means none. */
       int AVATag[MAX_AVA];        /* ATTRTAG_PRINTABLE_STRING, etc. */
       char AVAValues[MAX_AVA][MAX_NAME_LENGTH + 1];    /* C strings */
       short RDNIndexStart[MAX_RDN]; /* index into AVAs for ea. RDN. */
       short RDNIndexEnd[MAX_RDN];                 /* -1 means none. */
     } DistinguishedNameStruct;


                                       11


                 RIPEM API LIBRARY REFERENCE: Function Overview



     Typically, you need to access a distinguished name returned by RIPEM,
     such as in a CertificateStruct returned by DERToCertificate or the
     userDN in RIPEMInfo set during RIPEMLoginUser. The simplest
     information to get about a distinguished name is the "smart name",
     which is defined by GetDNSmartNameIndex in the next chapter. (Usually,
     the smart name is the common name.) To get the index of the AVA which
     is the smart name, use GetDNSmartNameIndex which lets you get the
     attribute type, attribute value and its tag. But usually, you only
     care about the value, so you can use the "convenience" function
     GetDNSmartNameValue.

     To scan all the information in a name, you can use the following code:

     DistinguishedNameStruct name;
     int rdn, ava;

     /* ... a call to a function which fills the name */

     /* Scan starting from the least significant RDN. */
     for (rdn = MAX_RDN - 1; rdn >= 0; --rdn) {
       if (name.RDNIndexStart[rdn] == -1)
         /* -1 means there is no RDN here, so try the next one */
         continue;

       /* scan the array of AVAs.  (If RDNIndexEnd != RDNIndexStart, then
            there are multiple AVAs in this RDN.) */
       for (ava = name.RDNIndexStart[rdn]; ava <= name.RDNIndexEnd[rdn];
            ++ava) {
         /* Now, name.AVATypes[ava] is the attribute type,
            name.AVAValues[ava] is the attribute value and
            name.AVATag[ava] is the value's tag. */
       }
     }

     The attribute values in AVAValues are null-terminated C strings. The
     attribute types in AVATypes are one of the ATTRTYPE_ values listed in
     ripem.h such as ATTRTYPE_COMMONNAME. The AVATag values are usually
     ATTRTAG_PRINTABLE_STRING or ATTRTAG_T61_STRING. However, it is
     possible for a value tag to contain an integer other than these, which
     you can display as "unrecognized."

     Sometimes you need to construct a new name, for example to create a
     new user by calling RIPEMGenerateKeys. Before adding values to the
     DistinguishedNameStruct, you should initialize the structure with
     InitDistinguisheNameStruct. Then you can add AVA information to the
     AVATypes, AVAValues and AVATag arrays. For each RDN level, set the
     entries in the RDNIndexStart and RDNIndexEnd arrays to the appropriate
     indexes in the AVA arrays. Remember that arrays are indexed starting
     from zero and that the first entry in the RDN array is the most
     significant level. When setting the AVATypes for a new name, you can
     use IsPrintableString to choose between ATTRTAG_PRINTABLE_STRING and
     ATTRTAG_T61_STRING.



                                       12


                 RIPEM API LIBRARY REFERENCE: Function Overview


     Attributes: The RIPEMAttributes Structure

     An attributes structure, as used in PKCS-compliant messages, is a
     collection of attributes. Each attribute has a type and one or more
     values. Many attribute types, such as the ones currently used by
     RIPEM, restrict the number of values to one.

     RIPEM uses the RIPEMAttributes structure to represent attributes,
     which is defined as follows:

     typedef struct {
       BOOL haveSigningTime;
       unsigned long signingTime; /* seconds since midnight 1/1/70 */
       BOOL haveSigningDescription;
       char *signingDescription;                       /* C string */
       BOOL haveChallengePassword;
       char *challengePassword;                        /* C string */
     } RIPEMAttributes;

     RIPEM supports attributes which can be returned from
     RIPEMDecipherPKCSFinal and attributes which can be supplied to
     RIPEMCertifyRequestPKCS. See the details on these functions in the
     next chapter for a description of these attributes. For each type,
     such as signingTime, there is a BOOL flag, such as haveSigningTime. If
     the flag is FALSE, then the associated value, such as signingTime, is
     undefined. If the flag is TRUE then the associated value is present.

     Sometimes you need to construct new attributes structure, for example
     to supply attributes to RIPEMCertifyRequestPKCS. Before adding
     attributes to the structure, you should initialize it with
     InitRIPEMAttributes. Then you can add the attributes you want.

     Decoding Certificates

     RIPEM uses certificates to validate a user's public key by creating a
     signed certificate which binds the user's public key to the user's
     distinguished name. Certificates are typically returned by RIPEM in
     the certChain list from SelectCertChain or as the logged-in user's
     self-signed certificate in the RIPEMInfo.

     When RIPEM returns a certificate, it is as a block of data in encoded
     form. To decode it, use DERToCertificate which uses a
     CertificateStruct structure to return the certificate contents.
     CertificateStruct is defined as follows:

     typedef struct CertificateStruct {
       unsigned int version;
       unsigned char serialNumber[16];        /* up to 128 bits. */
       int digestAlgorithm;
       DistinguishedNameStruct issuer;
       unsigned long notBefore;            /* seconds since 1970 */
       unsigned long notAfter;             /* seconds since 1970 */
       DistinguishedNameStruct subject;
       R_RSA_PUBLIC_KEY publicKey;


                                       13


                 RIPEM API LIBRARY REFERENCE: Function Overview


       unsigned char signature[MAX_SIGNATURE_LEN];
       int signatureLen;
     } CertificateStruct;

     For this release of RIPEM, the certificate version is one. (Don't be
     confused by the fact that a version of one is represented by the
     integer zero in the encoding.) The serialNumber array holds the
     certificate's serial number, most significant byte first, right
     justified and zero padded to the left out to 16 bytes. digestAlgorithm
     is one of the tokens defined by RSAREF such as DA_MD2 or DA_MD5. For
     example, if digestAlgorithm is DA_MD5, it means the certificate
     information was digested with MD5 and then encrypted with the issuer's
     private key to make the signature. You should also use the
     digestAlgorithm specified by the certificate when using R_DigestBlock
     to get a self-signed certificate digest.

     The issuer and subject names are represented with a
     DistinguishedNameStruct as defined in the previous section. notBefore
     is the beginning of the certificate's validity period and notAfter is
     the end. These are represented as number of seconds since January
     first, 1970 at midnight Greenwich Mean Time. publicKey is the
     certificate subject's public key represented as an R_RSA_PUBLIC_KEY as
     defined by RSAREF. Finally, the signature array contains the
     certificate's signature, where signatureLen is its length in bytes.

     Logging In and Managing User Keys

     RIPEM uses the RIPEMInfo structure to hold the information of a
     logged-in user. When you declare a RIPEMInfo structure, you must call
     RIPEMInfoConstructor before using it and RIPEMInfoDestructor when
     finished. For most RIPEM operations, you need to use RIPEMLoginUser to
     log in the user, which decrypts the user's private key, loads the
     user's self-signed certificate, etc. Use RIPEMChangePassword to change
     the password which encrypts the user's private key.

     To create a new user, you can use RIPEMGenerateKeys which generates a
     public/private keypair and other important information.

     Managing Certification Information

     RIPEM has several functions which let a user manage certification
     information such as validating other users by issuing certificates for
     them, hooking into certification hierarchies by setting chain length
     allowed for other issuers, revoking certificates, etc. When
     RIPEMDecipherFinal or RIPEMDecipherPKCSFinal returns the self-signed
     certificate of an unrecognized message sender, you can use
     ValidateAndWriteCert to create a new certificate for that user.

     You can use SelectCertChain to get the certificate chain for any user,
     for example to get that user's public key to use when encrypting a
     message, or to get the user's certificate serial number in order to
     revoke the user. SelectCertChain requires the distinguished name of
     the user in question, not just a the common name or "username". To get
     the distinguished name, you can use GetCertsBySmartname and then use


                                       14


                 RIPEM API LIBRARY REFERENCE: Function Overview


     DERToCertificate to decode the certificate which contains the
     distinguished name. (See GetDNSmartNameIndex in the next chapter for a
     definition of "smart name".) Note that GetCertsBySmartname may return
     multiple matches for a given smart name such as "common name = bob,
     organization = Gadgets" and "common name = Bob, organization =
     Widgets", so you need to make sure the application user picks the
     correct one.

     You can use SetChainLenAllowed to set the chain length allowed of
     another user, and GetChainLenAllowed to get the current chain length
     allowed. These operations identify the user in question by the digest
     of that user's public key, which you should compute using
     GetPublicKeyDigest.

     Each RIPEM user maintains a certificate revocation list (CRL). If the
     CRL expires (causing a CRL EXPIRED certificate status) you can use
     RIPEMUpdateCRL to update the CRL issued by the logged-in user.
     RIPEMUpdateCRL will also create a new CRL if one doesn't exist. By
     passing the serial number of another user, you can also use
     RIPEMUpdateCRL to revoke that user by adding the serial number to the
     CRL.

     If the logged-in user's CRL is of interest to others, you can create a
     CRL message which can be sent to others. To create such a CRL message
     in PEM format, use RIPEMPublishCRLInit, RIPEMPublishCRLUpdate and
     RIPEMPublishCRLFinal. To create such a CRL message in PKCS format, use
     RIPEMCertsAndCRL_PKCSInit, RIPEMCertsAndCRL_PKCSUpdate and
     RIPEMCertsAndCRL_PKCSFinal. If the logged-in user's certificates are
     of interest to others, these functions can also be used add any or all
     of the certificates in the database to the message which can be sent
     to others.

     If the user's certification preferences become corrupt (as indicated
     by RIPEMLoginUser), you can use RIPEMSavePreferences to save a fresh
     copy of the preferences in the database.

     Use RIPEMCertifyRequestPKCS to create a PKCS-compliant certification
     request which can be sent to a certification authority to obtain a
     certificate. (Note that this certification request message is not a
     self-signed certificate. To send a self-signed certificate to another
     user in a PKCS-compliant message, simply use RIPEMEncipherPKCSInit,
     RIPEMEncipherPKCSUpdate and RIPEMEncipherPKCSFinal as described in the
     next section.)

     Message Processing

     The general init/update/final structure of message processing in RIPEM
     is explained in the RSIGN example in the introduction. Use
     RIPEMEncipherInit, RIPEMEncipherDigestUpdate, RIPEMEncipherUpdate and
     RIPEMEncipherFinal to create a PEM-compliant MIC-CLEAR, MIC-ONLY or
     ENCRYPTED message. Use RIPEMDecipherInit, RIPEMDecipherUpdate and
     RIPEMDecipherFinal to decipher a PEM-compliant message which may be a
     CRL message as well as MIC-CLEAR, MIC-ONLY or ENCRYPTED.



                                       15


                 RIPEM API LIBRARY REFERENCE: Function Overview


     Use RIPEMEncipherPKCSInit, RIPEMEncipherPKCSUpdate and
     RIPEMEncipherPKCSFinal to create a PKCS-compliant signed, enveloped or
     signed and enveloped message. Use RIPEMDecipherPKCSInit,
     RIPEMDecipherPKCSUpdate and RIPEMDecipherPKCSFinal to decipher a PKCS-
     compliant message which may be a certs-and-CRLs-only message as well
     as signed, enveloped or signed and enveloped. Note that you can use
     RIPEMCertsAndCRL_PKCSInit, RIPEMCertsAndCRL_PKCSUpdate and
     RIPEMCertsAndCRL_PKCSFinal (as mentioned in the previous section) to
     create a PKCS-compliant certs-and-CRLs-only message.

     To create a PEM-compliant CRL retrieval request message, use
     RIPEMRequestCRLsInit, RIPEMRequestCRLsUpdate and
     RIPEMRequestCRLsFinal.

     RIPEM also supports _detached_ PKCS signatures where the message data
     is external to the PKCS signature information. Use
     RIPEMSignDetachedPKCSInit, RIPEMSignDetachedPKCSDigestUpdate and
     RIPEMSignDetachedPKCSFinal to create a PKCS-compliant detached
     signature. Use RIPEMVerifyDetachedPKCSInit,
     RIPEMVerifyDetachedPKCSDigestUpdate, RIPEMVerifyDetachedPKCSUpdate and
     RIPEMVerifyDetachedPKCSFinal to verify a PKCS-compliant detached
     signature.

     Utility Functions

     RIPEM represents time in seconds since January first, 1970 at midnight
     Greenwich Mean Time. You can use R_time to get the current time.

     R_realloc provides a more robust version of the standard realloc (such
     as freeing the buffer if it can't be reallocated.)

     RIPEM functions return an error using a message string, however RSAREF
     functions do not. You can use FormatRSAError to convert an RSAREF
     error token into a message string.

     CrackLine is a utility function to parse a string of comma-delimited
     items into a list of separate items. This is useful for parsing
     environment variables containing multiple items.

     CrackRecipients is a utility function to parse a string of comma-
     delimited email addresses into a list of separate items. This also
     extracts the user@domain portions from addresses like "
     jefft@netcom.com (Jeff Thompson)". This is useful for getting names of
     recipients in the To: field of an outgoing message in order to encrypt
     the message for them.

     Compatibility with Early Versions of RIPEM

     Early versions of RIPEM kept user public keys sitting alone and
     unvalidated instead of being in signed certificates. SelectCertChain
     will only return public keys which are in certificates. Therefore, if
     you need to be compatible with early versions of RIPEM, you can use
     GetUnvalidatedPublicKey to select these type of unvalidated public
     keys.


                                       16


                 RIPEM API LIBRARY REFERENCE: Function Overview


     Instead of using a RIPEM home directory, early versions of RIPEM
     required the user to explicitly specify input and output files for
     public and private keys. InitRIPEMDatabase will automatically open the
     public and private key files in the RIPEM home directory, but if you
     need to specify extra files which were created with early versions of
     RIPEM, you can use AddKeySourceFilename.

     RIPEMGenerateKeys will automatically create a self-signed certificate
     for the new user. However, if a user is upgrading from an early
     version of RIPEM which did not have self-signed certificates, you can
     use WriteSelfSignedCert to create one.













































                                       17





                        RIPEM API FUNCTION DETAILS

     This chapter lists the RIPEM API functions alphabetically. All of
     these functions are prototyped in ripem.h. A typical RIPEM application
     also uses some functions from RSAREF which are detailed in Appendix A.


   char *AddKeySourceFilename (TypKeySource *keySource, char *filename);

     This adds the filename to the beginning of keySource's filelist. This
     copies the filename, so you do not need to preserve the string pointed
     to by filename after this is called. If filename is already in
     keySource's filelist, this does nothing. keySource points to a
     TypKeySource which should be either pubKeySource, privKeySource or
     crlSource within a RIPEMDatabase structure. (You must construct the
     RIPEMDatabase using RIPEMDatabaseConstructor before calling this.)
     This does not actually open the file given by filename. After you have
     used this to add one or more filenames to pubKeySource, privKeySource
     and crlSource in the RIPEMDatabase, you should call InitRIPEMDatabase
     to actually open the files.

     Typically, you do not need to call AddKeySourceFilename. This is only
     provided for compatibility with RIPEM versions 1.1 and earlier where
     the user manipulated public and private key files outside the RIPEM
     home directory.

     Returns: (char *)NULL for success, otherwise an error string.


   char *AddToList (TypListEntry *prevEntry, void *entry, unsigned int
     entryLen, TypList *list);

     This is a generic TypList handling function to add an entry to the
     list. Usually, you would call a more specific function like
     AppendLineToList. (See "Lists: The TypList Structure" in the previous
     chapter for a full discussion on handling lists.) You must initialize
     list using InitList before calling this. entry is a pointer to an
     allocated data block of length entryLen. This does not make a copy of
     the data pointed to by entry. However, it is assumed that entry points
     to data which you have allocated with malloc: be aware that FreeList
     will first zeroize the data block and then call free on it.

     If prevEntry is (TypListEntry *)NULL, then the entry is added to the
     end if the list. Otherwise, prevEntry must be one of the existing
     TypListEntry nodes in the list, and the entry is inserted into the
     list after prevEntry. (To insert an entry at the beginning of the
     list, use PrependToList.)

     Returns: (char *)NULL for success, otherwise an error string.


   char *AppendLineToList (char *line, TypList *list);




                                       18


                 RIPEM API LIBRARY REFERENCE: Function Details


     This adds the line to the end of the list. (See "Lists: The TypList
     Structure" in the previous chapter for a full discussion on handling
     lists.) line is a null-terminated string. You must initialize list
     using InitList before calling this. This copies the line, so you do
     not need to preserve the string pointed to by line after this is
     called.

     Returns: (char *)NULL for success, otherwise an error string.


   char *CrackLine (char *line, TypList *valList);

     This is a utility function to parse a line of delimited items and to
     return each as a separate item in a list. line is a null-terminated
     string containing items which are delimited by a comma or a '\n', such
     as "A, B \n C". You must initialize valList using InitList before
     calling this. Before processing the line, this calls FreeList on
     valList to make sure it is empty. Each item is added to valList as a
     null-terminated string entry. line itself is not modified by this
     function.

     Whitespace before and after each item is removed, where whitespace is
     defined as ' ', '\t', or '\n'. (Even though '\n' is considered
     whitespace, it is also considered a delimiter between items.) If the
     length of any item is more than 1023 characters, it is broken into
     separate 1023 character items (due to an internal buffer length
     limit).

     Returns: (char *)NULL for success, otherwise an error string.


   char *CrackRecipients (char *line, TypList *recipientNames);

     This is a utility function to parse a line of delimited email
     addresses and to return each as a separate item in a list. line is a
     null-terminated string containing email addresses which are delimited
     by a comma or a '\n'. You must initialize recipientNames using
     InitList before calling this. This does not call FreeList on
     recipientNames. Therefore, CrackRecipients may be called repeatedly to
     add more names to recipientNames. Each email address is added to
     recipientNames as a null-terminated string entry. line itself is not
     modified by this function. This is meant to be used to get usernames
     from To: and cc: fields from an email message you are encrypting in
     order to look up the recipients' certificates and public keys.

     Whitespace before and after each email address is removed, where
     whitespace is defined as ' ', '\t', or '\n'. This extracts the
     user@domain portion from addresses like president@whitehouse.gov (The
     Prez) or "Mark Riordan" <riordanmr@clvax1.cl.msu.edu> and places only
     that portion in recipientNames.

     Returns: (char *)NULL for success, otherwise an error string.




                                       19


                 RIPEM API LIBRARY REFERENCE: Function Details


   int DERToCertificate (unsigned char *der, CertificateStruct *cert,
     CertFieldPointers *fieldPointers);

     This parses the certificate, returning the component parts in a
     CertificateStruct. (See "Decoding Certificates" in the previous
     chapter for details of the CertificateStruct type.) der points to the
     buffer containing the encoded certificate. You should declare a
     CertificateStruct and pass a pointer to it in cert, which is filled
     with the certificate components. On return, fieldPointers->innerDER
     points inside the der buffer to the beginning of the "inner" portion
     of the certificate and fieldPointers->innerDERLen is the length of the
     "inner" portion. (The inner DER is the data to be verified when
     checking the certificate signature. This is also the data which you
     should digest with MD5, using R_DigestBlock, to present a "self-signed
     certificate digest.") If fieldPointers is (CertFieldPointers *)NULL,
     it is ignored.

     Returns: a negative value for any parsing error. Otherwise returns the
     total length of the der encoding.


   char *FormatRSAError (int errorCode);

     This is a utility function to covert an integer error return code from
     RSAREF to a error string in the style returned by RIPEM. For example,
     when called with an errorCode of RE_DIGEST_ALGORITHM, this returns
     "Message-digest algorithm is invalid". If called with an errorCode of
     0 or an unrecognized value, this returns "Unknown error returned from
     RSAREF routines".


   void FreeList (TypList *list);

     This is the complement to InitList. You must call this when you are
     done with the TypList structure pointed to by list. This zeroizes all
     entry buffers in the list and calls free on each. This also returns
     list to the state it was in after the first call to InitList, so the
     list can be used again if necessary. Do not call FreeList for a list
     unless you have previously called InitList on it.


   char *GetCertsBySmartname (RIPEMDatabase *ripemDatabase, TypList *certs,
     char *smartName, RIPEMInfo *ripemInfo);

     This searches every file listed in ripemDatabase->pubKeySource for
     certificate records where the User: field matches the given value of
     smartName. This is called "smartName" because when RIPEM originally
     writes a certificate to the database, it uses the smart name of the
     certificate's subject distinguished name in the User: field. Each
     matching certificate is added to the TypList pointed to by certs. You
     must initialize the certs list using InitList before calling this.
     ripemInfo is used only for accessing the debugStream.

     Returns: (char *)NULL for success, otherwise an error string.


                                       20


                 RIPEM API LIBRARY REFERENCE: Function Details


   unsigned int GetChainLenAllowed (RIPEMInfo *ripemInfo, unsigned char
     *publicKeyDigest);

     This returns the chain length allowed for the user with the key given
     by publicKeyDigest. The public key in question usually comes from a
     user's certificate in a certificate chain. To get the public key
     digest, you should decode the certificate using DERToCertificate and
     pass the public key from the CertificateStruct to GetPublicKeyDigest.

     This uses publicKeyDigest as an index into the certificate preferences
     in ripemInfo which were loaded during RIPEMLoginUser. If the public
     key digest is not found, this returns zero as the default, otherwise
     it returns the specified chain length allowed.


   unsigned int GetDNSmartNameIndex (DistinguishedNameStruct *name);

     This locates the "smart name" in the array of AVAs in name and returns
     its index. (See "Distinguished Names" in the previous chapter for a
     full discussion "distinguished name" and "AVA".) The returned value
     can be used as an index into name's AVATypes, AVATag and AVAValues
     arrays.

     A distinguished name is usually big, such as " common name =
     fred@snark.edu, org unit = Persona Certificate, organization = RSA
     Data Security, Inc., country = US". Therefore, RIPEM defines the
     concept of a smart name which is the most distinctive of the
     distinguished name's attributes. (Typically, this is the common name.)
     The smart name is used to look up certificates, and (through looking
     up certificates) to identify the user during log in (-u in the RIPEM
     command line application) and to identify recipients of encrypted
     messages (-r in the RIPEM command line application).

     RIPEM chooses a distinguished name's smart name in the following
     manner: If there is a common name AVA in the distinguished name, the
     smart name is the least significant common name. ("Least significant"
     is defined in "Handling Distinguished Names and Certificates" in the
     previous chapter.) If there is no common name, the smart name is the
     least significant title AVA. If there are no common name or title
     attributes, the smart name is the least significant AVA in the
     distinguished name, whatever that is.


   char *GetDNSmartNameValue (DistinguishedNameStruct *name);

     This returns the value of the distinguished name's smart name by
     calling GetDNSmartNameIndex and returning a pointer to the resulting
     AVAValue in the name. In other words, this is a function provided for
     convenience which returns name->AVAValues[GetDNSmartNameIndex (name)].
     The return value is a null-terminated C string. See the description of
     GetDNSmartNameIndex for a definition of "smart name".





                                       21


                 RIPEM API LIBRARY REFERENCE: Function Details


   char *GetPublicKeyDigest (unsigned char *digest, R_RSA_PUBLIC_KEY
     *publicKey);

     This returns the MD5 digest of the DER encoding of publicKey. The
     digest buffer which receives the digest must be at least 16 bytes
     long. This function is necessary for a subtle reason: There are
     multiple object identifiers for an RSA key that people use for the DER
     encoding of the public key, and so it is possible for the digest of
     the same public key to come out differently if the object identifier
     is different. Since many RIPEM functions, such as GetChainLenAllowed,
     rely on the public key digest to be a unique identifier of the public
     key, you should always use GetPublicKeyDigest to obtain it (as
     opposed, for example, to directly digesting the encoded public key
     yourself). GetPublicKeyDigest always encodes the publicKey with the
     same object identifier before computing the digest ("rsa" as opposed
     to "rsaEncoding", to be specific).

     Returns: (char *)NULL for success, otherwise an error string.


   char *GetUnvalidatedPublicKey (char *user, TypKeySource *source,
     R_RSA_PUBLIC_KEY *key, BOOL *found, RIPEMInfo *ripemInfo);

     This is provided only as a fallback for compatibility with RIPEM 1.1
     and earlier in the case that a public key for a user cannot be found
     through the normal method of calling SelectCertChain. If your
     application requires a public key to be validated within a certificate
     instead of just sitting out "in the raw", then you should not use this
     function.

     user is the username whose public key you are looking for. source
     should point to the pubKeySource in the RIPEMDatabase which is already
     initialized with InitRIPEMDatabase. If the key is found, this sets
     found to TRUE and copies the public key to the R_RSA_PUBLIC_KEY
     pointed to by key. Otherwise, this sets found to FALSE and the
     R_RSA_PUBLIC_KEY is undefined. ripemInfo is used only for accessing
     the debugStream.

     Returns: (char *)NULL for success, otherwise an error string. Note
     that for the simple case that the key for user is not found, this only
     sets found to FALSE and returns (char *)NULL.


   void InitDistinguishedNameStruct (DistinguishedNameStruct *name);

     This initializes the DistinguishedNameStruct pointed to by name by
     pre-zerozing and then setting all the AVATypes and RDN indexes to -1.
     This also presets all AVATag to ATTRTAG_PRINTABLE_STRING. You should
     always call InitDistinguishedNameStruct before constructing a new name
     so that the AVATypes and RDN indexes are -1 by default, and all you
     have to do is add the needed values. Also, when each
     DistinguishedNameStruct is pre-zeroized to the same initial value in
     this manner, two name structures with the same information can easily
     be compared with the bit-wise = = operator.


                                       22


                 RIPEM API LIBRARY REFERENCE: Function Details


   void InitList (TypList *list);

     This initializes the TypList pointed to by list by setting up its
     pointers for an empty list. You should call this on any TypList
     structure you create before using it. (If the RIPEM API were C++, this
     would be the constructor for this type.) You must also call FreeList
     when you are done using the TypList.


   void InitRIPEMAttributes (RIPEMAttributes *attributes);

     This initializes the RIPEMAttributes structure pointed to by
     attributes by zerozing which sets all _have_ flags to FALSE. You
     should always call InitRIPEMAttributes before preparing attributes to
     pass in to a function such as RIPEMCertifyRequestPKCS. In this way,
     all attributes are absent by default and only the ones you add by
     setting haveChallengePassword, for example, will be present.


   char *InitRIPEMDatabase (RIPEMDatabase *ripemDatabase, char *homeDir,
     RIPEMInfo *ripemInfo);

     This initializes ripemDatabase by opening the database files in the
     RIPEM home directory given by homeDir. You must construct
     ripemDatabase using RIPEMDatabaseConstructor before calling this.
     ripemInfo is used only for accessing the debugStream. The directory
     name given by homeDir must end in the appropriate directory separator
     for your operating system, such as '/' in Unix or a backslash in DOS.
     This is so that RIPEM can construct a full pathname for a file in the
     RIPEM home directory simply by appending the filename to homeDir.

     You are also responsible for making sure the RIPEM home directory
     exists before calling InitRIPEMDatabase, since RIPEM is capable of
     creating new files but not new directories. Here is a simple way to
     test for the existence of the directory before calling
     InitRIPEMDatabase: Append "crls" to the homeDir and to use fopen to
     try to open it in append mode "a". If the directory exists, the crls
     file will be opened if the file exists, or created if the file does
     not exist (which is all right). If the directory does not exist, fopen
     will return (FILE *)NULL in which case you should use your platform's
     operating system call to create the directory. (Note that on most
     platforms, you need to make sure there is no directory separator at
     the end of the directory name when you create the directory.)

     When you call InitRIPEMDatabase, pubKeySource, privKeySource and
     crlSource in ripemDatabase may already have filenames which you added
     with AddKeySourceFilename (only for compatibility with RIPEM versions
     1.1 and earlier). InitRIPEMDatabase adds the filenames pubkeys,
     privkey and crls in the RIPEM home directory if they are not already
     in ripemDatabase. This also makes sure each of these files can be
     opened for output and that each is the first entry listed in their
     respective TypKeySource list. (The first file listed is the one RIPEM
     uses for output.) This then uses fopen to open all the files for read.



                                       23


                 RIPEM API LIBRARY REFERENCE: Function Details


     This also sets ripemDatabase->preferencesFilename to preferen in the
     RIPEM home directory and makes sure it can be opened for output.

     Returns: (char *)NULL for success, otherwise an error string.


   int IsPrintableString (unsigned char *valuePointer, unsigned int
     valueLen);

     This scans the buffer pointed to by valuePointer, of length valueLen,
     and returns 1 if all the characters are in the PrintableString
     character set, otherwise it returns 0. You should use this when
     constructing a new distinguished name: if IsPrintableString returns 1,
     you should set the AVATag to ATTRTAG_PRINTABLE_STRING, otherwise you
     should set it to ATTRTAG_T61_STRING.


   char *PrependToList (void *entry, unsigned int entryLen, TypList *list);

     This is a generic TypList handling function to add an entry to the
     beginning of the list. This function is a necessary companion to
     AddToList since, when the prevEntry parameter to AddToList is NULL, it
     will add the entry to the end of the list. (See "Lists: The TypList
     Structure" in the previous chapter for a full discussion on handling
     lists.) You must initialize list using InitList before calling this.
     entry is a pointer to an allocated data block of length entryLen. This
     does not make a copy of the data pointed to by entry. However, it is
     assumed that entry points to data which you have allocated with
     malloc: be aware that FreeList will first zeroize the data block and
     then call free on it.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMCertCursorInit (RIPEMDatabaseCursor *cursor, char *smartName,
     RIPEMDatabase *ripemDatabase);

     This prepares the RIPEMDatabaseCursor structure pointed to by cursor
     to search for certificates in every file listed in
     ripemDatabase->pubKeySource. You must construct cursor using
     RIPEMDatabaseCursorConstructor and initialize ripemDatabase using
     InitRIPEMDatabase before calling this.

     If smartName is (char *)NULL, this will cause RIPEMCertCursorUpdate to
     find all certificates in the database. Otherwise, this will cause
     RIPEMCertCursorUpdate to find certificate records in the database
     where the User: field matches the given value of smartName. This is
     called "smartName" because when RIPEM originally writes a certificate
     to the database, it uses the smart name of the certificate's subject
     distinguished name in the User: field.

     Returns: (char *)NULL for success, otherwise an error string.




                                       24


                 RIPEM API LIBRARY REFERENCE: Function Details


   char *RIPEMCertCursorUpdate (RIPEMDatabaseCursor *cursor, BOOL *found,
     TypList *certs, RIPEMDatabase *ripemDatabase, RIPEMInfo *ripemInfo);

     This selects the next certificate from ripemDatabase according to the
     search criteria set up by RIPEMCertCursorUpdate. ripemInfo is used
     only for accessing the debugStream.

     You must declare a BOOL and pass a pointer to it in found. If there
     are no more matching certificates, this sets found FALSE. Otherwise,
     this sets found TRUE and the matching certificate is added to the
     TypList structure pointed to by certs. You must initialize the certs
     list using InitList before calling this. You may keep calling
     RIPEMCertCursorUpdate until it sets found to FALSE.

     You should not call any other functions which use ripemDatabase in
     between calls to RIPEMCertCursorUpdate.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMCertifyRequestPKCS (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen, RIPEMAttributes *attributes);

     Use this to create a PKCS-compliant certification request message
     (PKCS #10) for the user logged in to ripemInfo. Before calling this,
     you must call RIPEMLoginUser to properly initialize ripemInfo.

     attributes may point to a RIPEMAttributes structure which contains
     attributes to add to the certification request. If
     haveChallengePassword is TRUE, then challengePassword points to a
     null-terminated C string containing the challenge password. You are
     responsible for allocating the memory that challengePassword points
     to. Other attributes in the RIPEMAttributes structure are ignored.

     attributes may be (RIPEMAttributes *)NULL in which case no attributes
     are used.

     To obtain the output, you should declare an unsigned char * and pass a
     pointer to this in partOut, and also declare an unsigned int and pass
     a pointer to this in partOutLen, so that RIPEM can return the pointer
     and length of a working buffer which contains the output. This buffer
     is allocated by RIPEM and maintained within ripemInfo. The buffer
     pointer returned in partOut is only valid until the next call to
     RIPEM, so it must be written out or copied immediately. Also, you do
     not need to free the memory buffer returned in partOut. This is done
     by RIPEMInfoDestructor. On error return, the pointer to the output is
     undefined.

     The output is _as is._ No translation of `\n' to <CR><LF> should be
     done because the PKCS message format is binary.

     (Note that this certification request message is not a self-signed
     certificate. To send a self-signed certificate to another user in a



                                       25


                 RIPEM API LIBRARY REFERENCE: Function Details


     PKCS-compliant message, simply use RIPEMEncipherPKCSInit,
     RIPEMEncipherPKCSUpdate and RIPEMEncipherPKCSFinal.)

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMCertsAndCRL_PKCSFinal (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen, BOOL includeSenderCerts, BOOL
     includeCRL, RIPEMDatabase *ripemDatabase);

     Use this to add issuer certificates or a CRL to a PKCS-compliant
     certs-and-CRLs-only message and to produce the final output. You
     should already have called RIPEMCertsAndCRL_PKCSInit and called
     RIPEMCertsAndCRL_PKCSUpdate zero or more times. ripemInfo points to
     the same structure which was initialized in RIPEMCertsAndCRL_PKCSInit.

     If includeSenderCerts is TRUE, this adds the self-signed certificate
     for the user logged in to ripemInfo and any issuer certificates for
     the logged-in user. If includeCRL is true, this adds the CRL for the
     user by getting it from the database. (If neither includeSenderCerts
     or includeCRL is TRUE, the only use of this message is if certificates
     were added with RIPEMCertsAndCRL_PKCSUpdate.)

     If includeCRL is TRUE, this returns an error if the CRL cannot be
     found or the signature is corrupt. Otherwise, the CRL is used even if
     it is expired.

     The output is returned in partOut and partOutLen as in
     RIPEMCertsAndCRL_PKCSInit. RIPEMCertsAndCRL_PKCSFinal should only be
     called once per message.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMCertsAndCRL_PKCSInit (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *);

     This initializes a PKCS-compliant certs-and-CRLs-only message can
     contain the CRL for the user logged in to ripemInfo, the certificates
     for the logged in user and any other certificates from the database.
     Before calling this, you must call RIPEMLoginUser to properly
     initialize ripemInfo.

     To obtain the output, you should declare an unsigned char * and pass a
     pointer to this in partOut, and also declare an unsigned int and pass
     a pointer to this in partOutLen, so that RIPEM can return the pointer
     and length of a working buffer which contains the output. This buffer
     is allocated by RIPEM and maintained within ripemInfo. The buffer
     pointer returned in partOut is only valid until the next call to
     RIPEM, so it must be written out or copied immediately. Also, you do
     not need to free the memory buffer returned in partOut. This is done
     by RIPEMInfoDestructor. On error return, the pointer to the output is
     undefined.



                                       26


                 RIPEM API LIBRARY REFERENCE: Function Details


     The output is _as is._ No translation of `\n' to <CR><LF> should be
     done because the PKCS message format is binary.

     After calling this, you should call RIPEMCertsAndCRL_PKCSUpdate zero
     or more times to add extra certificates, and
     RIPEMCertsAndCRL_PKCSFinal output the CRL or issuer certificates and
     to finish.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMCertsAndCRL_PKCSUpdate (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen, TypList *certs);

     Call this zero or more times to add extra certificates to a PKCS-
     compliant certs-and-CRLs-only message. You should already have called
     RIPEMCertsAndCRL_PKCSInit. ripemInfo points to the same structure
     which was initialized in RIPEMCertsAndCRL_PKCSInit. The output is
     returned in partOut and partOutLen as in RIPEMCertsAndCRL_PKCSInit.

     Each entry in the certs list is added to the certs-and-CRLs-only
     message. Note that this can be called an arbitrarily large number of
     times without overrunning memory. You can use RIPEMCertCursorUpdate to
     repeatedly select a certificate from the database and then add it to
     the message. If you do this, then you can use FreeList to clear the
     certs list after it has been written to the message, allowing you to
     select more certificates from the database.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMChangePassword (RIPEMInfo *ripemInfo, unsigned char
     *newPassword, unsigned int newPasswordLen, RIPEMDatabase
     *ripemDatabase);

     This re-encrypts the user's private key with a new password and writes
     it to the first private key file listed privKeySource in
     ripemDatabase. Before calling this, you must call RIPEMLoginUser to
     properly initialize ripemInfo and to load the private key. The new
     password is supplied in the newPassword buffer of length
     newPasswordLen.

     When the private key is re-written to the private key file, the smart
     name of ripemInfo->userDN is used in a User: field to identify it. If
     ripemInfo->z.usernameAliases is not NULL, then it points to a TypList
     where each entry is a null-terminated string containing an alias for
     the username and each is also written in a User: field. The username
     aliases are only useful for RIPEM version 1.1 and earlier when the
     recipient of a message was identified by an email address instead of
     the public key. If you do not require compatibility with version 1.1
     and earlier, then you may leave ripemInfo->z.usernameAliases NULL as
     it is initialized by RIPEMInfoConstructor.




                                       27


                 RIPEM API LIBRARY REFERENCE: Function Details


     This also calls RIPEMSavePreferences so that the preferences are re-
     authenticated under the new password. Therefore, this will return an
     error if ripemDatabase->preferencesFilename cannot be opened.

     Returns: (char *)NULL for success, otherwise an error string.


   void RIPEMDatabaseConstructor (RIPEMDatabase *ripemDatabase);

     This initializes the RIPEMDatabase structure pointed to by
     ripemDatabase. You should call this on any RIPEMDatabase structure you
     create before using it. (If the RIPEM API were C++, this would be the
     constructor for this type.) You must also call RIPEMDatabaseDestructor
     when you are done using the RIPEMDatabase.


   void RIPEMDatabaseCursorConstructor (RIPEMDatabaseCursor *cursor);

     This initializes the RIPEMDatabaseCursor structure pointed to by
     cursor. You should call this on any RIPEMDatabaseCursor structure you
     create before using it. (If the RIPEM API were C++, this would be the
     constructor for this type.) You must also call
     RIPEMDatabaseCursorDestructor when you are done using the
     RIPEMDatabaseCursor.


   void RIPEMDatabaseCursorDestructor (RIPEMDatabaseCursor *cursor);

     This finalizes the RIPEMDatabaseCursor structure pointed to by cursor.
     This is the complement to RIPEMDatabaseCursorConstructor. You must
     call this when you are done with the RIPEMDatabaseCursor structure.
     (If the RIPEM API were C++, this would be the destructor for this
     type.)


   void RIPEMDatabaseDestructor (RIPEMDatabase *ripemDatabase);

     This finalizes the RIPEMDatabase structure pointed to by ripemDatabase
     by closing all the files in pubKeySource, privKeySource and crlSource.
     This is the complement to RIPEMDatabaseConstructor. You must call this
     when you are done with the RIPEMDatabase structure. (If the RIPEM API
     were C++, this would be the destructor for this type.)


   char *RIPEMDecipherFinal (RIPEMInfo *ripemInfo, TypList *certChain,
     ChainStatusInfo *chainStatus, enum enhance_mode *enhanceMode);

     Use this to finalize the processing of a PEM-compliant message which
     you are deciphering. You should already have called
     RIPEMDecipherUpdate one or more times to process the message.
     ripemInfo points to the same structure which was initialized in
     RIPEMDecipherInit. You must initialize a TypList using InitList and
     pass a pointer to it in certChain. You must also declare a
     ChainStatusInfo structure and pass a pointer to it in chainStatus.


                                       28


                 RIPEM API LIBRARY REFERENCE: Function Details


     This returns the sender's certificate chain in certChain and the
     certification status of the certificates in chainStatus.

     You must declare an enum enhance_mode and pass a pointer to it in
     enhanceMode. RIPEM uses this to return the enhancement mode of the
     message which is MODE_ENCRYPTED for an encrypted message,
     MODE_MIC_ONLY for a mic-only message, MODE_MIC_CLEAR for a mic-clear
     message or MODE_CRL for a CRL message. For a MODE_CRL, certChain is
     unmodified since there are no senders, and chainStatus->overall is set
     to zero. In all these cases, any certificates or CRLs in the message
     are added to the database. RIPEM checks that the signature from the
     issuer of CRLs is valid, but does not check the signature on
     certificates. (This is done more efficiently by SelectCertChain when
     retrieving the certificates.)

     If enhanceMode is not MODE_CRL and chainStatus->overall is zero, RIPEM
     could not find a trusted certificate for the sender. In this case, if
     the message contained a self-signed certificate from the sender and
     certChain contains one entry which is the self-signed certificate.
     (You may check for an entry in certChain by checking if
     certChain->firstptr is non-null.) This would typically happen when
     receiving a message from an unknown sender which the application user
     may want to validate. You may decode the certificate using
     DERToCertificate and present the self-signed certificate digest to the
     user who may choose whether to validate the certificate. (Use
     R_DigestBlock with the digest algorithm specified in the certificate
     to digest the certificate's inner DER.) To validate, see
     ValidateAndWriteCert. On the other hand, if there is no entry in
     certChain (certChain->firstptr is null) then there was no self-signed
     certificate in the message, which means that the sender is completely
     unrecognized. You may suggest to the user that the sender provide a
     message with a self-signed certificate.

     If chainStatus->overall is CERT_UNVALIDATED, RIPEM could not find a
     trusted certificate for the sender, but it could find an unvalidated
     public key. In this case, certChain contains one entry which is the
     sender's username. This is provided only for compatibility with RIPEM
     version 1.1 and earlier which did not use certificates. If you do not
     wish to support unvalidated public keys, then treat this like the
     completely unrecognized sender as above.

     For other values of chainStatus->overall, certChain and chainStatus
     contain values as described in SelectCertChain. Note that the sender
     name is the subject name of the first certificate listed in certChain.

     Returns: (char *)NULL for success, otherwise an error string. There is
     a special error strings which RIPEMDecipherFinal may return:
     ERR_NO_PEM_HEADER_BEGIN. (You should use strcmp to compare the error
     return string to the value.) You may wish to "catch" this special
     error cases and give it special treatment as follows:

     If this returns ERR_NO_PEM_HEADER_BEGIN, then the string _-----BEGIN
     PRIVACY-ENHANCED MESSAGE-----_ could not be found. This string marks
     the beginning of the cryptographic information in a PEM message. The


                                       29


                 RIPEM API LIBRARY REFERENCE: Function Details


     message may be in PKCS format and you may wish to try to process the
     message using RIPEMDecipherPKCSInit, etc.


   char *RIPEMDecipherInit (RIPEMInfo *ripemInfo, BOOL prependHeaders);

     This initializes ripemInfo for deciphering a PEM-compliant message.
     Before calling this, you must call RIPEMLoginUser to properly
     initialize ripemInfo. If prependHeaders is TRUE, RIPEM will copy email
     headers (encountered before the begin enhanced message boundary) from
     the input message through to the output. Otherwise, prependHeaders
     should be FALSE.

     After calling RIPEMDecipherInit, you should call RIPEMDecipherUpdate
     to decipher the message by parts, and RIPEMDecipherFinal to finish.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMDecipherPKCSFinal (RIPEMInfo *ripemInfo, TypList *certChain,
     ChainStatusInfo *chainStatus, int *pkcsMode, RIPEMAttributes
     *authenticatedAttributes);

     Use this to finalize the processing of a PKCS-compliant message which
     you are deciphering. You should already have called
     RIPEMDecipherPKCSUpdate one or more times to process the message.
     ripemInfo points to the same structure which was initialized in
     RIPEMDecipherPKCSInit. You must initialize a TypList using InitList
     and pass a pointer to it in certChain. You must also declare a
     ChainStatusInfo structure and pass a pointer to it in chainStatus.
     This returns the sender's certificate chain in certChain and the
     certification status of the certificates in chainStatus.

     You must declare an int and pass a pointer to it in pkcsMode. RIPEM
     uses this to return the PKCS mode of the message which is PKCS_SIGNED
     for a signed message, PKCS_ENVELOPED for an enveloped-only message,
     PKCS_SIGNED | PKCS_ENVELOPED for a signed and enveloped message or
     PKCS_CERTS_AND_CRLS_ONLY for a certs-and-CRLs-only message. For
     PKCS_ENVELOPED and PKCS_CERTS_AND_CRLS_ONLY, certChain is unmodified
     since there are no senders, and chainStatus->overall is set to zero.
     In all these cases, any certificates or CRLs in the message are added
     to the database. RIPEM checks that the signature from the issuer of
     CRLs is valid, but does not check the signature on certificates. (This
     is done more efficiently by SelectCertChain when retrieving the
     certificates.)

     If pkcsMode is PKCS_SIGNED or PKCS_SIGNED | PKCS_ENVELOPED and
     chainStatus->overall is zero, RIPEM could not find a trusted
     certificate for the sender. In this case, the message contained a
     self-signed certificate from the sender and certChain contains one
     entry which is the self-signed certificate. This would typically
     happen when receiving a message from an unknown sender which the
     application user may want to validate. You may decode the certificate
     using DERToCertificate and present the self-signed certificate digest


                                       30


                 RIPEM API LIBRARY REFERENCE: Function Details


     to the user who may choose whether to validate the certificate. (Use
     R_DigestBlock with the digest algorithm specified in the certificate
     to digest the certificate's inner DER.) To validate, see
     ValidateAndWriteCert.

     RIPEMDecipherPKCSFinal does not support a chain status of
     CERT_UNVALIDATED as RIPEMDecipherFinal does, since PKCS-compliant
     messages always use a certificate to identify senders and recipients.

     For other values of chainStatus->overall, certChain and chainStatus
     contain values as described in SelectCertChain. Note that the sender
     name is the subject name of the first certificate listed in certChain.

     If authenticatedAttributes is not NULL, then it points to a
     RIPEMAttributes structure which receives the authenticated attributes
     in the message, if any.  Upon return, if haveSigningTime is TRUE, then
     signingTime contains the signing time represented as the number of
     seconds since January first, 1970 at midnight Greenwich Mean Time.  If
     haveSigningDescription is TRUE, then signingDescription points to a
     null-terminated C string containing the signing description. The
     memory for the signingDescription is allocated inside ripemInfo and
     should be treated as "read only".  Other attributes in the
     RIPEMAttributes structure are undefined. Upon return, the value of an
     attribute must be copied before a future call to RIPEM since RIPEM may
     modify it.

     authenticatedAttributes may be (RIPEMAttributes *)NULL, in which case
     it is ignored.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMDecipherPKCSInit (RIPEMInfo *ripemInfo);

     This initializes ripemInfo for deciphering a PKCS-compliant message.
     Before calling this, you must call RIPEMLoginUser to properly
     initialize ripemInfo.

     After calling RIPEMDecipherPKCSInit, you should call
     RIPEMDecipherPKCSUpdate to decipher the message by parts, and
     RIPEMDecipherPKCSFinal to finish.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMDecipherPKCSUpdate (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen, unsigned char *partIn, unsigned
     int partInLen, RIPEMDatabase *ripemDatabase);

     Use this to process a PKCS-compliant message which you are deciphering
     and to produce the output which is decoded and decrypted as necessary.
     You should already have called RIPEMDecipherPKCSInit. ripemInfo points
     to the same structure which was initialized in RIPEMDecipherPKCSInit.



                                       31


                 RIPEM API LIBRARY REFERENCE: Function Details


     ripemDatabase must already be initialized using InitRIPEMDatabase and
     is used to process the certificate information in the message.

     partIn points to the buffer of the input message to process, of length
     partInLen. partIn may contain any number of bytes.

     To obtain the output from each call to RIPEMDecipherPKCSUpdate, you
     should declare an unsigned char * and pass a pointer to this in
     partOut, and also declare an unsigned int and pass a pointer to this
     in partOutLen, so that RIPEMDecipherPKCSUpdate can return the pointer
     and length of a working buffer which contains the output. This buffer
     is allocated by RIPEM and maintained within ripemInfo. The buffer
     pointer returned in partOut is only valid until the next call to
     RIPEM, so it must be written out or copied immediately. Also, you do
     not need to free the memory buffer returned in partOut. This is done
     by RIPEMInfoDestructor. On error return, the pointer to the output is
     undefined.

     The input and output are _as is._ No translation of `\n' to <CR><LF>
     is done because the PKCS message format can support binary data. The
     calling routine must do end-of-line character translation on the
     output text if necessary.

     You can process a message of unlimited length by calling this multiple
     times by reading in a part of the message and calling
     RIPEMDecipherPKCSUpdate, reading in the next part, calling again, etc.
     When you are done processing the message in this manner, proceed to
     use RIPEMDecipherPKCSFinal.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMDecipherUpdate (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen, unsigned char *partIn, unsigned
     int partInLen, RIPEMDatabase *ripemDatabase);

     Use this to process a PEM-compliant message which you are deciphering
     and to produce the output which is decoded and decrypted as necessary.
     You should already have called RIPEMDecipherInit. ripemInfo points to
     the same structure which was initialized in RIPEMDecipherInit.
     ripemDatabase must already be initialized using InitRIPEMDatabase and
     is used to process the certificate information in the message header.

     partIn points to the buffer of the input message to process, of length
     partInLen. Textual lines must be delimited by the '\n' character (not
     <CR><LF>). This can be done, for example, by using fgets or fread to
     read from a file which has been opened in text mode "r". partIn may
     contain parts of lines or multiple lines.

     To obtain the output from each call to RIPEMDecipherUpdate, you should
     declare an unsigned char * and pass a pointer to this in partOut, and
     also declare an unsigned int and pass a pointer to this in partOutLen,
     so that RIPEMDecipherUpdate can return the pointer and length of a
     working buffer which contains the output. This buffer is allocated by


                                       32


                 RIPEM API LIBRARY REFERENCE: Function Details


     RIPEM and maintained within ripemInfo. The buffer pointer returned in
     partOut is only valid until the next call to RIPEM, so it must be
     written out or copied immediately. Also, you do not need to free the
     memory buffer returned in partOut. This is done by
     RIPEMInfoDestructor. On error return, the pointer to the output is
     undefined.

     Textual lines in the output are delimited by the character '\n'. You
     must convert these to your platform's local line delimiter, which is
     done automatically if you use fwrite to write the output to a file
     which has been opened in text mode "w".

     You can process a message of unlimited length by calling this multiple
     times by reading in a part of the message and calling
     RIPEMDecipherUpdate, reading in the next part, calling again, etc.
     When you are done processing the message in this manner, proceed to
     use RIPEMDecipherFinal.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMEncipherDigestUpdate (RIPEMInfo *ripemInfo, unsigned char
     *partIn, unsigned int partInLen);

     Use this to digest the text of a PEM-compliant message which you are
     enciphering. You should already have called RIPEMEncipherInit.
     ripemInfo points to the same structure which was initialized in
     RIPEMEncipherInit. partIn points to the buffer of text to digest, of
     length partInLen. Textual lines must be delimited by the '\n'
     character (not <CR><LF>). This can be done, for example, by using
     fgets or fread to read from a file which has been opened in text mode
     "r". Make sure there is a '\n' at the end of the final line, which is
     ensured on most platforms if the file is read using fgets.

     You can process text of unlimited length by calling this multiple
     times by reading in a part of the text and calling
     RIPEMEncipherDigestUpdate, reading in the next part, calling again,
     etc. When you are done digesting the text in this manner, proceed to
     use RIPEMEncipherUpdate.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMEncipherFinal (RIPEMInfo *ripemInfo, unsigned char **partOut,
     unsigned int *partOutLen, RIPEMDatabase *ripemDatabase);

     Use this to finalize the processing of a PEM-compliant message which
     you are enciphering and to produce the final part of the output. You
     should already have called RIPEMEncipherUpdate one or more times to
     process the text. (If neither RIPEMEncipherDigestUpdate or
     RIPEMEncipherUpdate has been called, this will still output a message
     with empty text.) ripemInfo points to the same structure which was
     initialized in RIPEMEncipherInit. ripemDatabase must already be
     initialized using InitRIPEMDatabase and is used for selecting


                                       33


                 RIPEM API LIBRARY REFERENCE: Function Details


     certificates to find issuer names and serial numbers of recipients
     when using MESSAGE_FORMAT_PEM. The output is returned in partOut and
     partOutLen as in RIPEMEncipherUpdate. RIPEMEncipherFinal should only
     be called once per message.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMEncipherInit (RIPEMInfo *ripemInfo, enum enhance_mode
     enhanceMode, int messageFormat, int encryptionAlgorithm,
     RecipientKeyInfo *recipientKeys, unsigned int recipientKeyCount);

     This initializes ripemInfo for enciphering a PEM-compliant message.
     Before calling this, you must call RIPEMLoginUser to properly
     initialize ripemInfo. enhanceMode must be MODE_ENCRYPTED,
     MODE_MIC_ONLY, or MODE_MIC_CLEAR.

     If enhanceMode is MODE_ENCRYPTED, then encryptionAlgorithm must be
     EA_DES_CBC or EA_DES_EDE2_CBC. ripemInfo->randomStruct must be seeded
     (see Appendix A for RSAREF documentation on R_RandomUpdate). Also,
     recipientKeys must point to an array of RecipientKeyInfo structures of
     length recipientKeyCount (one for each message recipient).
     RecipientKeyInfo is defined as:

     typedef struct {
       R_RSA_PUBLIC_KEY publicKey;
       char *username;
     } RecipientKeyInfo;

     The publicKey holds the public key of the message recipient. You
     should obtain the recipient's public key by using SelectCertChain and
     using DERToCertificate to decode the user's certificate which contains
     the public key. The username in the RecipientKeyInfo points to a
     string containing the recipient's username. The username is used for
     backward compatibility with RIPEM 1.1 (under MESSAGE_FORMAT_RIPEM1
     mode) and for looking up the recipient's issuer name and serial number
     (under MESSAGE_FORMAT_PEM mode). The array pointed to by recipientKeys
     (and the strings pointed to by each username) must remain valid until
     the first call to RIPEMEncipherUpdate.

     Also, for MODE_ENCRYPTED, note that you should add an entry in
     recipientKeys for the user logged in to ripemInfo (the -T m option in
     the RIPEM command line application). This is so that after creating an
     encrypted message, the user can decrypt it later if necessary. To make
     a recipientKey entry for the logged-in user, set the publicKey to
     ripemInfo->publicKey and set the username to
     GetDNSmartNameValue(&ripemInfo->userDN).

     messageFormat must be MESSAGE_FORMAT_RIPEM1 or MESSAGE_FORMAT_PEM.
     MESSAGE_FORMAT_RIPEM1 is compatible with all versions of RIPEM
     including those before 2.0. It has a Proc-Type version of 2001 and
     includes the Originator-Name field and uses Recipient-Key-Asymmetric
     (containing the public key) for the recipients of encrypted messages.
     MESSAGE_FORMAT_PEM is for compatibility with the RFC 1421 standards


                                       34


                 RIPEM API LIBRARY REFERENCE: Function Details


     suite. It has a Proc-Type version of 4 and omits RIPEM-specific fields
     (such as Originator-Name) and uses only Recipient-ID-Asymmetric
     (containing issuer name and serial number) for the recipients of
     encrypted messages. (See the section "Specifying Message Format" in
     the RIPEM user manual and the document RIPEMFMT.TXT for more details.)

     After calling RIPEMEncipherInit, you should call
     RIPEMEncipherDigestUpdate to digest the text by parts,
     RIPEMEncipherUpdate to enhance the text by parts, and
     RIPEMEncipherFinal to finish.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMEncipherPKCSFinal (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen, RIPEMAttributes
     *authenticatedAttributes);

     Use this to finalize the processing of a PKCS-compliant message which
     you are enciphering and to produce the final part of the output. You
     should already have called RIPEMEncipherPKCSUpdate one or more times
     to process the text. (If RIPEMEncipherPKCSUpdate has not been called,
     this will still output a message with empty text.) ripemInfo points to
     the same structure which was initialized in RIPEMEncipherPKCSInit. You
     should pass (RIPEMAttributes *)NULL for authenticatedAttributes, which
     is provided for future compatibility.

     The output is returned in partOut and partOutLen as in
     RIPEMEncipherPKCSUpdate. RIPEMEncipherPKCSFinal should only be called
     once per message.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMEncipherPKCSInit (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen, int pkcsMode, int
     encryptionAlgorithm, RecipientKeyInfo *recipientKeys, unsigned int
     recipientKeyCount, RIPEMDatabase *ripemDatabase);

     This initializes ripemInfo for enciphering a PKCS-compliant message.
     Before calling this, you must call RIPEMLoginUser to properly
     initialize ripemInfo. pkcsMode must be PKCS_SIGNED for a signed
     message, PKCS_ENVELOPED for an enveloped message, or PKCS_SIGNED |
     PKCS_ENVELOPED for a signed and enveloped message. ripemDatabase must
     already be initialized using InitRIPEMDatabase and is used for
     selecting certificates to find issuer names and serial numbers of
     recipients.

     If pkcsMode is PKCS_ENVELOPED or PKCS_SIGNED | PKCS_ENVELOPED, then
     encryptionAlgorithm must be EA_DES_CBC or EA_DES_EDE3_CBC.
     ripemInfo->randomStruct must be seeded (see Appendix A for RSAREF
     documentation on R_RandomUpdate). Also, recipientKeys must point to an
     array of RecipientKeyInfo structures of length recipientKeyCount (one
     for each message recipient). RecipientKeyInfo is defined as:


                                       35


                 RIPEM API LIBRARY REFERENCE: Function Details


     typedef struct {
       R_RSA_PUBLIC_KEY publicKey;
       char *username;
     } RecipientKeyInfo;

     The publicKey holds the public key of the message recipient. You
     should obtain the recipient's public key by using SelectCertChain and
     using DERToCertificate to decode the user's certificate which contains
     the public key. The username in the RecipientKeyInfo points to a
     string containing the recipient's username. The username is used for
     looking up the recipient's issuer name and serial number.

     Also, for PKCS_ENVELOPED or PKCS_SIGNED | PKCS_ENVELOPED, note that
     you should add an entry in recipientKeys for the user logged in to
     ripemInfo (the -T m option in the RIPEM command line application).
     This is so that after creating an encrypted message, the user can
     decrypt it later if necessary. To make a recipientKey entry for the
     logged-in user, set the publicKey to ripemInfo->publicKey and set the
     username to GetDNSmartNameValue(&ripemInfo->userDN).

     To obtain the output from RIPEMEncipherPKCSInit, you should declare an
     unsigned char * and pass a pointer to this in partOut, and also
     declare an unsigned int and pass a pointer to this in partOutLen, so
     that RIPEMEncipherPKCSInit can return the pointer and length of a
     working buffer which contains the output. This buffer is allocated by
     RIPEM and maintained within ripemInfo. The buffer pointer returned in
     partOut is only valid until the next call to RIPEM, so it must be
     written out or copied immediately. Also, you do not need to free the
     memory buffer returned in partOut. This is done by
     RIPEMInfoDestructor. On error return, the pointer to the output is
     undefined.

     After calling RIPEMEncipherPKCSInit, you should call
     RIPEMEncipherPKCSUpdate to enhance the text by parts, and
     RIPEMEncipherPKCSFinal to finish.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMEncipherPKCSUpdate (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen, unsigned char *partIn, unsigned
     int partInLen);

     Use this to process the text of a PKCS-compliant message which you are
     enciphering and to produce the output which is signed and encrypted as
     necessary. ripemInfo points to the same structure which was
     initialized in RIPEMEncipherPKCSInit. partIn points to the buffer of
     text to process, of length partInLen. The output is returned in
     partOut and partOutLen as in RIPEMEncipherPKCSInit.

     The input and output are _as is._ No translation of `\n' to <CR><LF>
     is done because the PKCS message format can support binary data. The
     calling routine must do end-of-line character translation on the input
     if necessary before passing it to RIPEMEncipherPKCSUpdate.


                                       36


                 RIPEM API LIBRARY REFERENCE: Function Details


     You can process text of unlimited length by calling this multiple
     times by reading in a part of the text and calling
     RIPEMEncipherPKCSUpdate, reading in the next part, calling again, etc.
     When you are done digesting the text in this manner, proceed to use
     RIPEMEncipherPKCSFinal.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMEncipherUpdate (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen, unsigned char *partIn, unsigned
     int partInLen, RIPEMDatabase *ripemDatabase);

     Use this to process the text of a PEM-compliant message which you are
     enciphering and to produce the output which is encoded and encrypted
     as necessary. You should already have called RIPEMEncipherDigestUpdate
     one or more times. ripemInfo points to the same structure which was
     initialized in RIPEMEncipherInit. ripemDatabase must already be
     initialized using InitRIPEMDatabase and is used for selecting
     certificates to find issuer names and serial numbers of recipients
     when using MESSAGE_FORMAT_PEM. partIn points to the buffer of text to
     process, of length partInLen. Textual lines must be delimited by the
     '\n' character as in RIPEMEncipherDigestUpdate.

     To obtain the output from each call to RIPEMEncipherUpdate, you should
     declare an unsigned char * and pass a pointer to this in partOut, and
     also declare an unsigned int and pass a pointer to this in partOutLen,
     so that RIPEMEncipherUpdate can return the pointer and length of a
     working buffer which contains the output. This buffer is allocated by
     RIPEM and maintained within ripemInfo. The buffer pointer returned in
     partOut is only valid until the next call to RIPEM, so it must be
     written out or copied immediately. Also, you do not need to free the
     memory buffer returned in partOut. This is done by
     RIPEMInfoDestructor. On error return, the pointer to the output is
     undefined.

     Textual lines in the output are delimited by the character '\n'. You
     must convert these to your platform's local line delimiter, which is
     done automatically if you use fwrite to write the output to a file
     which has been opened in text mode "w".

     You can process text of unlimited length by calling this multiple
     times just as you did for RIPEMEncipherDigestUpdate. On the first call
     to RIPEMEncipherUpdate, the output contains the message header. And of
     course the data that you pass to RIPEMEncipherUpdate should be the
     exact same data you passed to RIPEMEncipherDigestUpdate. This means
     that if the input is coming from a file, you must remember to rewind
     the file between the final call to RIPEMEncipherDigestUpdate and the
     first call to RIPEMEncipherUpdate. When you are done processing the
     text in this manner, proceed to use RIPEMEncipherFinal.

     Returns: (char *)NULL for success, otherwise an error string.




                                       37


                 RIPEM API LIBRARY REFERENCE: Function Details


   char *RIPEMGenerateKeys (RIPEMInfo *ripemInfo, unsigned int bits,
     unsigned int validityMonths, unsigned char *password, unsigned int
     passwordLen, RIPEMDatabase *ripemDatabase);

     This generates a public/private keypair with a modulus of the given
     number of bits and creates a self-signed certificate. You must
     construct ripemInfo using RIPEMInfoConstructor before calling this.
     ripemInfo->randomStruct must be seeded (see Appendix A for RSAREF
     documentation on R_RandomUpdate). ripemInfo->userDN should be set to
     the new user's distinguished name. (See "Distinguished Names " in the
     previous chapter for details on constructing a distinguished name.)
     ripemDatabase must already be initialized by InitRIPEMDatabase.

     When the private key or self-signed certificate is written to the
     database files, the smart name of ripemInfo->userDN is used in a User:
     field to identify it. Also, if ripemInfo->z.usernameAliases is not
     NULL, then it points to a TypList where each entry is a null-
     terminated string containing an alias for the username and each is
     also written in a User: field for the private key. The username
     aliases are only useful for RIPEM version 1.1 and earlier when the
     recipient of a message was identified by an email address instead of
     the public key. If you do not require compatibility with version 1.1
     and earlier, then you may leave ripemInfo->z.usernameAliases NULL as
     it is initialized by RIPEMInfoConstructor.

     This also writes the user's initial default preferences. Therefore,
     this will return an error if ripemDatabase->preferencesFilename cannot
     be opened.

     Returns: (char *)NULL for success, otherwise an error string.


   void RIPEMInfoConstructor (RIPEMInfo *ripemInfo);

     This initializes the RIPEMInfo structure pointed to by ripemInfo. You
     should call this on any RIPEMInfo structure you create before using
     it. (If the RIPEM API were C++, this would be the constructor for this
     type.) You must also call RIPEMInfoDestructor when you are done using
     the RIPEMInfo.

     RIPEMInfoConstructor sets ripemInfo->debug to zero by default. If you
     want a higher level of debug messages, you must set debug to the level
     and set ripemInfo->debugStream to the output stream for the debug
     messages.

     Note that some of the data members of the RIPEMInfo structure are
     within a struct named z for "zeroized". This is done so that
     RIPEMInfoConstructor can efficiently pre-zeroize these data members
     simply by zeroizing the z structure.


   void RIPEMInfoDestructor (RIPEMInfo *ripemInfo);




                                       38


                 RIPEM API LIBRARY REFERENCE: Function Details


     This finalizes the RIPEMInfo structure pointed to by ripemInfo by
     zeroizing sensitive data such as the private key and freeing buffers
     and lists which were allocated during the use of the RIPEMInfo. Note
     that this does not close the file pointed to by ripemInfo->debugStream
     since this is opened by the calling application. Nor does this free
     the TypList pointed to by ripemInfo->userList (if the application sets
     it) since this is created by the calling application.
     RIPEMInfoDestructor is the complement to RIPEMInfoConstructor. You
     must call this when you are done with the RIPEMInfo structure. (If the
     RIPEM API were C++, this would be the destructor for this type.)


   char *RIPEMLoginUser (RIPEMInfo *ripemInfo, char *username,
     RIPEMDatabase *ripemDatabase, unsigned char *password, unsigned int
     passwordLen);

     This initializes ripemInfo with a user's public key, private key and
     other information. You must construct ripemInfo using
     RIPEMInfoConstructor and initialize ripemDatabase using
     InitRIPEMDatabase before calling this. username should be the smart
     name of the user's distinguished name. Typically, this is the common
     name attribute of the distinguished name. (See the description of
     GetDNSmartNameIndex for a definition of "smart name".)

     RIPEM locates the first record in ripemDatabase's private key files
     where the User: field matches the supplied username. RIPEM then uses
     the supplied password, of length passwordLen, to decrypt the private
     key and places it in ripemInfo->privateKey. RIPEM then constructs the
     user's public key from the private key and places it in
     ripemInfo->publicKey. To find the user's self-signed certificate,
     RIPEM locates the first record in ripemDatabase's public key files
     where the User: field matches the username, the associated
     certificate's public key is the user's public key, and the certificate
     is a valid self-signed certificate (with the same issuer and subject
     name). The user's distinguished name is placed in ripemInfo->userDN
     and the encoded self-signed certificate is placed in
     ripemInfo->z.userCertDER of length ripemInfo->z.userCertDERLen. RIPEM
     also loads the user's preferences from ripemDatabase's preference
     file.

     This returns (char *)NULL for success, otherwise an error string.
     There are some special error strings which RIPEMLoginUser may return:
     ERR_PREFERENCES_NOT_FOUND, ERR_PREFERENCES_CORRUPT and
     ERR_SELF_SIGNED_CERT_NOT_FOUND. (You should use strcmp to compare the
     error return string to the value such as ERR_PREFERENCES_NOT_FOUND.)
     You may wish to "catch" any of these special error cases and give it
     special treatment as follows:

     If this returns ERR_PREFERENCES_NOT_FOUND then the user has been
     successfully logged in, but the entry in the preferences file for the
     user doesn't exist. In this case, you should alert the application
     user that default preferences will be used. The preferences will be
     saved in the preferences file by the next call to RIPEMSavePreferences
     or other functions like SetChainLenAllowed which save the preferences.


                                       39


                 RIPEM API LIBRARY REFERENCE: Function Details


     If the user believes that the preferences should have been there, the
     user may wish to abort since this may mean the preferences file has
     been tampered with. However, if the user is upgrading from RIPEM 1.2,
     ERR_PREFERENCES_NOT_FOUND is a normal condition since RIPEM 1.2 did
     not use preferences.

     If this returns ERR_PREFERENCES_CORRUPT, then the user has been
     successfully logged in, but the preferences information decodes badly
     or the signature doesn't verify. In this case, you should alert the
     application user that default preferences will be used and any
     previous preferences must be set again. New preferences will be saved
     by the next call to RIPEMSavePreferences or other functions like
     SetChainLenAllowed which save the preferences.

     If this returns ERR_SELF_SIGNED_CERT_NOT_FOUND, ripemInfo->publicKey
     and ripemInfo->privateKey have already been set in ripemInfo, but
     RIPEM could not find the user's self-signed certificate. This is an
     error condition unless you are trying to upgrade the user from RIPEM
     1.1 which did not have self-signed certificates. In this case, you may
     set ripemInfo->userDN and call WriteSelfSignedCert. (See the
     description of WriteSelfSignedCert.) If your application does not
     support upgrading from RIPEM 1.1, you should treat
     ERR_SELF_SIGNED_CERT_NOT_FOUND as a fatal error.


   char *RIPEMPublishCRL (RIPEMInfo *ripemInfo, unsigned char **output,
     unsigned int *outputLen, int messageFormat, RIPEMDatabase
     *ripemDatabase);

     This produces a PEM-compliant CRL message containing the CRL for the
     user logged in to ripemInfo. Before calling this, you must call
     RIPEMLoginUser to properly initialize ripemInfo and InitRIPEMDatabase
     to initialize ripemDatabase.

     To obtain the output, you should declare an unsigned char * and pass a
     pointer to this in output, and also declare an unsigned int and pass a
     pointer to this in outputLen, so that RIPEMPublishCRL can return the
     pointer and length of a working buffer which contains the output. This
     buffer is allocated by RIPEM and maintained within ripemInfo. The
     buffer pointer returned in output is only valid until the next call to
     RIPEM, so it must be written out or copied immediately. Also, you do
     not need to free the memory buffer returned in output. This is done by
     RIPEMInfoDestructor. On error return, the pointer to the output is
     undefined.

     See RIPEMPublishCRLInit for the meaning of messageFormat.

     RIPEMPublishCRL internally just calls RIPEMPublishCRLInit and
     RIPEMPublishCRLFinal. It is provided for backward compatibility with
     earlier versions of the RIPEM API.

     Returns: (char *)NULL for success, otherwise an error string.




                                       40


                 RIPEM API LIBRARY REFERENCE: Function Details


   char *RIPEMPublishCRLFinal (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen);

     Use this to produce the final output of a PEM-compliant CRL message.
     You should already have called RIPEMPublishCRLInit and called
     RIPEMPublishCRLUpdate zero or more times. ripemInfo points to the same
     structure which was initialized in RIPEMPublishCRLInit. The output is
     returned in partOut and partOutLen as in RIPEMPublishCRLInit.
     RIPEMPublishCRLFinal should only be called once per message.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMPublishCRLInit (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen, int messageFormat, RIPEMDatabase
     *ripemDatabase);

     This initializes a PEM-compliant CRL message containing the CRL for
     the user logged in to ripemInfo. Before calling this, you must call
     RIPEMLoginUser to properly initialize ripemInfo and InitRIPEMDatabase
     to initialize ripemDatabase.

     To obtain the output, you should declare an unsigned char * and pass a
     pointer to this in partOut, and also declare an unsigned int and pass
     a pointer to this in partOutLen, so that RIPEM can return the pointer
     and length of a working buffer which contains the output. This buffer
     is allocated by RIPEM and maintained within ripemInfo. The buffer
     pointer returned in partOut is only valid until the next call to
     RIPEM, so it must be written out or copied immediately. Also, you do
     not need to free the memory buffer returned in partOut. This is done
     by RIPEMInfoDestructor. On error return, the pointer to the output is
     undefined.

     This adds Originator-Certificate and Issuer-Certificate fields to the
     CRL message if possible. If messageFormat is MESSAGE_FORMAT_PEM and
     there is only one issuer chain, then the Originator-Certificate field
     in the message will have the certificate from that issuer. Otherwise,
     messageFormat should be MESSAGE_FORMAT_RIPEM1 and the Originator-
     Certificate field will have the user's self-signed certificate. (This
     is the same technique used when enciphering a message.)

     This returns an error if the CRL cannot be found or the signature is
     corrupt. Otherwise, the CRL is used even if it is expired.

     Textual lines in the output are delimited by the character '\n'. You
     must convert these to your platform's local line delimiter, which is
     done automatically if you use fwrite to write the output to a file
     which has been opened in text mode "w".

     After calling this, you should call RIPEMPublishCRLUpdate zero or more
     times to add extra certificates, and RIPEMPublishCRLFinal to finish.

     Returns: (char *)NULL for success, otherwise an error string.



                                       41


                 RIPEM API LIBRARY REFERENCE: Function Details


   char *RIPEMPublishCRLUpdate (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen, TypList *certs);

     Call this zero or more times to add extra certificates to a PEM-
     compliant CRL message. You should already have called
     RIPEMPublishCRLInit. ripemInfo points to the same structure which was
     initialized in RIPEMPublishCRLInit. The output is returned in partOut
     and partOutLen as in RIPEMPublishCRLInit.

     Each entry in the certs list is added as an _Issuer-Certificate_ field
     to the CRL message. This is not explicitly permitted by the PEM RFCs,
     but it is a way to export certificates for other users to import into
     their database. Note that this can be called an arbitrarily large
     number of times without overrunning memory. You can use
     RIPEMCertCursorUpdate to repeatedly select a certificate from the
     database and then add it to the CRL message. If you do this, then you
     can use FreeList to clear the certs list after it has been written to
     the message, allowing you to select more certificates from the
     database.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMRequestCRLsFinal (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen);

     Use this to produce the final output of a PEM-compliant CRL retrieval
     request. You should already have called RIPEMRequestCRLsUpdate one or
     more times. ripemInfo points to the same structure which was
     initialized in RIPEMRequestCRLsInit. The output is returned in partOut
     and partOutLen as in RIPEMRequestCRLsInit. RIPEMRequestCRLsFinal
     should only be called once per message.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMRequestCRLsInit (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen);

     This initializes ripemInfo for producing a PEM-compliant CRL retrieval
     request message. Before calling this, you must call RIPEMLoginUser to
     properly initialize ripemInfo.

     To obtain the output, you should declare an unsigned char * and pass a
     pointer to this in partOut, and also declare an unsigned int and pass
     a pointer to this in partOutLen, so that RIPEM can return the pointer
     and length of a working buffer which contains the output. This buffer
     is allocated by RIPEM and maintained within ripemInfo. The buffer
     pointer returned in partOut is only valid until the next call to
     RIPEM, so it must be written out or copied immediately. Also, you do
     not need to free the memory buffer returned in partOut. This is done
     by RIPEMInfoDestructor. On error return, the pointer to the output is
     undefined.



                                       42


                 RIPEM API LIBRARY REFERENCE: Function Details


     Textual lines in the output are delimited by the character '\n'. You
     must convert these to your platform's local line delimiter, which is
     done automatically if you use fwrite to write the output to a file
     which has been opened in text mode "w".

     After calling this, you should call RIPEMRequestCRLsUpdate to output
     the information for each requested CRL, and RIPEMRequestCRLsFinal to
     finish.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMRequestCRLsUpdate (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen, DistinguishedNameStruct *name);

     Use this to output the name of the issuer for the CRL you are
     requesting in a PEM-compliant CRL retrieval request message. The
     issuer is given by the DistinguishedNameStruct pointed to by name.
     Typically, this is the issuer in the CertificateStruct returned by
     DERToCertificate when decoding the certificates in a certificate
     chain. You should already have called RIPEMRequestCRLsInit. ripemInfo
     points to the same structure which was initialized in
     RIPEMRequestCRLsInit. The output is returned in partOut and partOutLen
     as in RIPEMRequestCRLsInit.

     To request multiple CRLs (such as one for the issuer of each
     certificate in a chain), you can call RIPEMRequestCRLsUpdate multiple
     times, passing the name of a different issuer each time.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMSavePreferences (RIPEMInfo *ripemInfo, RIPEMDatabase
     *ripemDatabase);

     This saves the preferences in ripemInfo by writing them to
     ripemDatabase->preferencesFilename. Before calling this, you must call
     RIPEMLoginUser to properly initialize ripemInfo and InitRIPEMDatabase
     to initialize ripemDatabase.

     You might want to call this to explicitly save the preferences if
     RIPEMLoginUser returns ERR_PREFERENCES_NOT_FOUND or
     ERR_PREFERENCES_CORRUPT. Note that RIPEM functions SetChainLenAllowed,
     RIPEMChangePassword and RIPEMUpdateCRL which modify the preferences
     implicitly save the preferences, so you do not need to call
     RIPEMSavePreferences in this case.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMSignDetachedPKCSFinal (RIPEMInfo *ripemInfo, unsigned char
     **partOut, unsigned int *partOutLen, RIPEMAttributes
     *authenticatedAttributes);



                                       43


                 RIPEM API LIBRARY REFERENCE: Function Details


     Use this to finalize the creating of a PKCS-compliant detached
     signature and to output the detached signature data. You should
     already have called RIPEMSignDetachedPKCSDigestUpdate one or more
     times to digest the text. (If RIPEMSignDetachedPKCSDigestUpdate has
     not been called, this will still output the signature of zero-length
     message.) ripemInfo points to the same structure which was initialized
     in RIPEMSignDetachedPKCSInit. You should pass (RIPEMAttributes *)NULL
     for authenticatedAttributes, which is provided for future
     compatibility.

     To obtain the output from RIPEMSignDetachedPKCSFinal, you should
     declare an unsigned char * and pass a pointer to this in partOut, and
     also declare an unsigned int and pass a pointer to this in partOutLen,
     so that RIPEMSignDetachedPKCSFinal can return the pointer and length
     of a working buffer which contains the output. This buffer is
     allocated by RIPEM and maintained within ripemInfo. The buffer pointer
     returned in partOut is only valid until the next call to RIPEM, so it
     must be written out or copied immediately. Also, you do not need to
     free the memory buffer returned in partOut. This is done by
     RIPEMInfoDestructor. On error return, the pointer to the output is
     undefined.

     RIPEMSignDetachedPKCSFinal should only be called once per signature.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMSignDetachedPKCSInit (RIPEMInfo *ripemInfo);

     This initializes ripemInfo for creating a PKCS-compliant detached
     signature. Before calling this, you must call RIPEMLoginUser to
     properly initialize ripemInfo.

     After calling RIPEMSignDetachedPKCSInit, you should call
     RIPEMSignDetachedPKCSDigestUpdate to digest the message data by parts,
     and RIPEMSignDetachedPKCSFinal to finish.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMSignDetachedPKCSDigestUpdate (RIPEMInfo *ripemInfo, unsigned
     char *partIn, unsigned int partInLen);

     Use this to digest the message data for a PKCS-compliant detached
     signature. ripemInfo points to the same structure which was
     initialized in RIPEMSignDetachedPKCSInit. partIn points to the buffer
     of message data to digest, of length partInLen.

     The input is "as is." No translation of `\n' to <CR><LF> is done
     because the PKCS message format can support binary data. The calling
     routine must do end-of-line character translation on the input if
     necessary before passing it to RIPEMSignDetachedPKCSDigestUpdate.




                                       44


                 RIPEM API LIBRARY REFERENCE: Function Details


     You can digest a message of unlimited length by calling this multiple
     times by reading in a part of the text and calling
     RIPEMSignDetachedPKCSDigestUpdate, reading in the next part, calling
     again, etc. When you are done digesting the text in this manner,
     proceed to use RIPEMSignDetachedPKCSFinal. (There is no
     RIPEMSignDetachedPKCSUpdate because RIPEMSignDetachedPKCSFinal
     produces the entire output.)

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMUpdateCRL (RIPEMInfo *ripemInfo, UINT4 nextUpdate, unsigned
     char *serialNumber, unsigned int serialNumberLen, RIPEMDatabase
     *ripemDatabase);

     This selects the current CRL for the user logged in to ripemInfo, sets
     the last update time to now and the next update time to nextUpdate,
     and inserts the updated CRL into the first file listed in
     ripemDatabase->crlSource. Before calling this, you must call
     RIPEMLoginUser to properly initialize ripemInfo and InitRIPEMDatabase
     to initialize ripemDatabase. The nextUpdate time is represented as the
     number of seconds since January first, 1970 at midnight Greenwich Mean
     Time. It is an error if nextUpdate is before now (as returned by
     R_time). This will create a new CRL if one does not exist in the
     database. This also sets the current CRL last update time in the RIPEM
     preferences to the new value and saves the modified preferences. (Note
     that this updates the CRL even if the CRL last update time in the
     RIPEM preferences doesn't match the actual latest CRL.)

     If serialNumber is not NULL, then serialNumber and serialNumberLen
     give the serial number of a user to revoke by adding an entry to the
     CRL. The revocation time for the revoked user is set to now. In this
     case, serialNumber typically points to the serialNumber buffer in the
     CertificateStruct returned by DERToCertificate, and serialNumberLen is
     the sizeof this buffer. However, if serialNumber is NULL,
     serialNumberLen is ignored and no new revocation entry is added. This
     is useful for renewing the CRL when it expires by writing a fresh one
     to the database.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMVerifyDetachedPKCSFinal (RIPEMInfo *ripemInfo, TypList
     *certChain, ChainStatusInfo *chainStatus, RIPEMAttributes
     *authenticatedAttributes);

     Use this to finalize the verifying of a PKCS-compliant detached
     signature. You should already have called
     RIPEMVerifyDetachedPKCSUpdate one or more times to supply the detached
     signature. ripemInfo points to the same structure which was
     initialized in RIPEMVerifyDetachedPKCSInit.





                                       45


                 RIPEM API LIBRARY REFERENCE: Function Details


     certChain, chainStatus and authenticatedAttributes are returned as in
     RIPEMDecipherPKCSFinal for a PKCS_SIGNED message. pkcsMode is not
     returned because it can only be PKCS_SIGNED.

     RIPEMVerifyDetachedPKCSFinal should only be called once per signature.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMVerifyDetachedPKCSInit (RIPEMInfo *ripemInfo, int
     digestAlgorithm);

     This initializes ripemInfo for verifying a PKCS-compliant detached
     signature. Before calling this, you must call RIPEMLoginUser to
     properly initialize ripemInfo. The digestAlgorithm is used by
     RIPEMVerifyDetachedPKCSDigestUpdate to digest the text and is also
     compared with the digest algorithm specified in the signature
     information. digestAlgorithm should be one of the tokens defined by
     RSAREF such as DA_MD2 or DA_MD5.

     After calling RIPEMVerifyDetachedPKCSInit, you should call
     RIPEMVerifyDetachedPKCSDigestUpdate to digest the message by parts,
     RIPEMVerifyDetachedPKCSUpdate to supply the detached signature, and
     RIPEMVerifyDetachedPKCSFinal to finish.

     Returns: (char *)NULL for success, otherwise an error string.


   char *RIPEMVerifyDetachedPKCSDigestUpdate (RIPEMInfo *ripemInfo,
     unsigned char *partIn, unsigned int partInLen);

     Use this to digest the message data of a PKCS-compliant detached
     signature which you are verifying. You should already have called
     RIPEMVerifyDetachedPKCSInit. ripemInfo points to the same structure
     which was initialized in RIPEMVerifyDetachedPKCSInit.

     partIn points to the buffer of the input message to digest, of length
     partInLen. partIn may contain any number of bytes.

     The input is _as is._ No translation of `\n' to <CR><LF> is done
     because the PKCS message format can support binary data. The calling
     routine must do end-of-line character translation on the output text
     if necessary.

     You can process a message of unlimited length by calling this multiple
     times by reading in a part of the message and calling
     RIPEMVerifyDetachedPKCSDigestUpdate, reading in the next part, calling
     again, etc. When you are done processing the message in this manner,
     proceed to use RIPEMVerifyDetachedPKCSUpdate.

     Returns: (char *)NULL for success, otherwise an error string.





                                       46


                 RIPEM API LIBRARY REFERENCE: Function Details


   char *RIPEMVerifyDetachedPKCSUpdate (RIPEMInfo *ripemInfo, unsigned char
     *partIn, unsigned int partInLen, RIPEMDatabase *ripemDatabase);

     Use this to supply the signature data of a PKCS-compliant detached
     signature which you are verifying. You should already have called
     RIPEMVerifyDetachedPKCSDigestUpdate to digest the message data. (If
     RIPEMVerifyDetachedPKCSDigestUpdate has not been called, this will
     still verify the signature of zero-length message.) ripemInfo points
     to the same structure which was initialized in
     RIPEMVerifyDetachedPKCSInit. ripemDatabase must already be initialized
     using InitRIPEMDatabase and is used to process the certificate
     information in the message.

     partIn points to the buffer of the input signature data to process, of
     length partInLen. partIn may contain any number of bytes.

     You can call this multiple times by reading in a part of the signature
     data and calling RIPEMVerifyDetachedPKCSUpdate, reading in the next
     part, calling again, etc. When you are done supplying the signature in
     this manner, proceed to use RIPEMVerifyDetachedPKCSFinal.

     Returns: (char *)NULL for success, otherwise an error string.


   void *R_realloc (void *pointer, unsigned int size);

     This is just like the normal realloc, except it frees the original
     pointer if realloc fails, whereas the normal realloc does not. Also,
     this returns a non-null value if size is zero, whereas some
     implementations may return (void *)NULL when sizing a block down to
     zero (which might be mistaken for an error return value). In this way,
     R_realloc is a little more robust that the normal realloc. It is used
     throughout the RIPEM library and is made available in the RIPEM API as
     a general utility function.

     If pointer is (void *)NULL, this behaves like malloc. Otherwise,
     pointer must point to a previously allocated block. On success, this
     returns the new block, otherwise it returns (void *)NULL.


   void R_time (UINT4 *theTime);

     This sets theTime to the number of seconds since January first, 1970
     at midnight Greenwich Mean Time. The type UINT4 is defined in global.h
     as unsigned long int. This representation of time is used by the RIPEM
     API in many places such as the beginning and end of a certificate
     validity period or the time of a CRL revocation entry (although these
     times are represented differently inside an encoded certificate or CRL
     as a "UTC Time"). Having a representation in seconds makes it easy to
     compare two times to see which is earlier or to compute a time
     interval by calculating the number of seconds in the interval.





                                       47


                 RIPEM API LIBRARY REFERENCE: Function Details


   char *SelectCertChain (RIPEMInfo *ripemInfo, TypList *certChain,
     ChainStatusInfo *chainStatus, DistinguishedNameStruct *name,
     R_RSA_PUBLIC_KEY *publicKey, BOOL directCertOnly, RIPEMDatabase
     *ripemDatabase);

     This selects the best certificate chain for the distinguished name
     pointed to by name. Typically, the name is the subject of the
     CertificateStruct returned by DERToCertificate. The RIPEM User Manual
     explains what is meant by the "best" certificate chain. Before calling
     this, you must call RIPEMLoginUser to properly initialize ripemInfo
     and InitRIPEMDatabase to initialize ripemDatabase.

     The certificate chain is returned in the TypList pointed to by
     certChain. You must declare this TypList and initialize it using
     InitList before calling SelectCertChain. SelectCertChain will call
     FreeList on certChain to make sure it is empty before searching for
     the chain. You must also declare a ChainStatusInfo structure and pass
     a pointer to it in chainStatus, which SelectCertChain uses to return
     the status of the certificates in the chain.

     On success, certChain contains the chain where each entry is the
     certificate DER. The first entry is the certificate for the user given
     by name (called the "bottom" certificate), and each successive entry
     is an issuer certificate going up the chain. The caller can use
     DERToCertificate to get the contents of the certificates in the chain.
     Note that DERToCertificate will not return an error since the
     certificate was already successfully decoded, so you can use it
     without worrying about the return value. The array for
     chainStatus->individual contains the individual certificate statues
     and corresponds to the certificates in certChain where
     chainStatus->individual[0] is the status for the certificate at the
     "bottom" of the chain, etc. chainStatus->overall contains the overall
     status, defined as the "worst" of the individual statues. The status
     (individual or overall) is one the CERT_ statuses, such as CERT_VALID,
     CERT_REVOCATION_UNKNOWN, etc. Note that an application is usually only
     interested in the overall chain status. The individual certificate
     statuses are provided only for extra detail.

     If publicKey is not NULL, it points to the public key for the user
     given by name, and limits the search to include only certificates for
     the user where the public key matches the supplied value. This is
     useful if you already have a certificate such as one returned by
     GetCertsBySmartName. To find out whether it is validated, you can call
     SelectCertChain, passing the public key in the certificate as well as
     the subject name. This is important because you don't want a
     certificate chain returned for the same name but a different public
     key.

     Also, if directCertOnly is FALSE, chain lengths up to
     MAX_CERT_CHAIN_LEN will be allowed. This is the usual case for finding
     senders and recipients of messages. But if directCertOnly is TRUE,
     SelectCertChain limits the certificate chain to only one certificate
     issued directly by the user which is logged in to ripemInfo. This is
     useful for operations such as revoking or setting chain length allowed


                                       48


                 RIPEM API LIBRARY REFERENCE: Function Details


     which are only meaningful on certificates issued directly by the
     logged-in user. (You don't want a longer certificate chain where the
     certificate for the user given by name is issued by someone else.)

     If no chain can be found, this sets chainStatus->overall to 0, the
     certChain is empty, values in chainStatus->individual are undefined,
     and the function return value is (char *)NULL.

     Returns: (char *)NULL for success, otherwise an error string. Note
     that failure to find a certificate chain does not result in an error
     string.


   char *SetChainLenAllowed (RIPEMInfo *ripemInfo, unsigned char
     *publicKeyDigest, unsigned int chainLenAllowed, RIPEMDatabase
     *ripemDatabase);

     This sets the chain length allowed for the user with the public key
     given by publicKeyDigest. This is the way that you specify that you
     trust a user, such as the Low Assurance Certification Authority, to
     certify other users. Before calling this, you must call RIPEMLoginUser
     to properly initialize ripemInfo. The public key in question usually
     comes from a user's certificate in a certificate chain. To get the
     public key digest, you should decode the certificate using
     DERToCertificate and pass the public key from the CertificateStruct to
     GetPublicKeyDigest.

     This modifies the certificate preferences in ripemInfo which were
     loaded during RIPEMLoginUser. If the public key digest is found, this
     modifies the entry to the given chainLenAllowed, otherwise it adds a
     new entry for the public key digest. However, if chainLenAllowed is
     zero, this removes the entry for the public key digest since zero is
     the default (and if there was no entry in the first place, this does
     not add one). This also calls RIPEMSavePreferences so that the stored
     preferences are updated. Therefore, this will return an error if
     ripemDatabase->preferencesFilename cannot be opened.

     Returns: (char *)NULL for success, otherwise an error string.


   char *ValidateAndWriteCert (RIPEMInfo *ripemInfo, CertificateStruct
     *certStruct, RIPEMDatabase *ripemDatabase);

     This is the function which you should use to validate another user for
     the first time. This creates a new certificate based in the
     information in certStruct and signs it with the private key in
     ripemInfo, then writes the new certificate to the database. Usually,
     this is done when RIPEMDecipherFinal returns a self-signed certificate
     from another user (indicated by a chainStatus->overall of zero). After
     verifying that the user given by the self-signed certificate should be
     validated (by making sure the self-signed certificate digest is
     correct) you can call ValidateAndWriteCert to make a new certificate
     which validates that user.



                                       49


                 RIPEM API LIBRARY REFERENCE: Function Details


     You can also call this in other circumstances where you might want to
     make a new certificate, like making a certificate for the Low
     Assurance Certification Authority in order to hook into the low
     assurance certificate hierarchy by setting its chain length allowed to
     2.

     When you call this, certStruct->subject should contain the
     distinguished name of the user to validate and certStruct->publicKey
     should contain that user's public key. certStruct->notBefore and
     certStruct->notAfter should contain the beginning and end times of the
     certificate's validity period. These represent the time in number of
     seconds since January first, 1970 at midnight Greenwich Mean Time. For
     example, you can get the begin time by calling R_time and the end time
     by adding the number of seconds in a year. It is an error if
     certStruct->notAfter is less than certStruct->notBefore.

     If the logged-in user in ripemInfo has already issued a certificate
     which has a current validity period (even if that certificate has been
     revoked) then this returns the error string
     ERR_CERT_ALREADY_VALIDATED. (You should compare the error return
     string to ERR_CERT_ALREADY_VALIDATED using strcmp.) Typically, this
     error string will not be returned because you should only call
     ValidateAndWriteCert to make a new certificate for a user after
     SelectCertChain fails to find a certificate for that user.

     This sets the certificate issuer to the logged-in user's distinguished
     name in ripemInfo and generates a unique serial number for this
     certificate by digesting the certificate data. This also sets the
     certificate version and digest algorithm to default values. This then
     signs the certificate with the private key in ripemInfo and adds the
     certificate to the first file listed in ripemDatabase->pubKeySource.
     The certificate is identified in the public key file by a User: field
     which is the smart name from certStruct->subject.

     Returns: (char *)NULL for success, otherwise an error string.


   char *WriteSelfSignedCert (RIPEMInfo *ripemInfo, unsigned int
     validityMonths, RIPEMDatabase *ripemDatabase);

     This creates a self-signed certificate using the ripemInfo->publicKey
     and ripemInfo->userDN with a validity->period starting from the
     current time to validityMonths in the future. The self-signed
     certificate is signed with ripemInfo->privateKey and written to the
     first file listed in ripemDatabase->pubKeySource.

     This function is provided only for applications which must upgrade a
     user from RIPEM 1.1 or earlier since these versions did not use self-
     signed certificates. And you should call this only in response to
     RIPEMLoginUser returning ERR_SELF_SIGNED_CERT_NOT_FOUND. If you do not
     need to convert RIPEM 1.1 users, you do not need to call
     WriteSelfSignedCert. (RIPEMGenerateKeys writes the new self-signed
     certificate itself, so you do not need to call WriteSelfSignedCert
     when creating a new user.)


                                       50


                 RIPEM API LIBRARY REFERENCE: Function Details


     When RIPEMLoginUser returns ERR_SELF_SIGNED_CERT_NOT_FOUND,
     ripemInfo->publicKey and ripemInfo->privateKey are already set. You
     must set ripemInfo->userDN and then call WriteSelfSignedCert. After
     that, the self-signed certificate is available for normal RIPEM
     operation.

     Returns: (char *)NULL for success, otherwise an error string.

















































                                       51





                   APPENDIX A: SELECTED RSAREF FUNCTIONS

     A typical RIPEM application uses some functions from RSAREF for random
     numbers and computing a message digest. Here are some relevant
     excerpts from the RSAREF document which is rsaref.txt in the RSAREF
     doc directory.

     Random Structures

     A random structure contains a seed from which a pseudorandom sequence
     of bytes is derived. RSAREF generates keys and pads RSA encryption
     blocks with bytes derived from a random structure.

     Random structures are used by both message-processing and
     key-generation applications.

     RSAREF sets up a random structure with the procedure R_RandomInit. A
     typical application calls R_RandomInit on entry.

     A new random structure is not ready for use until it is seeded by
     mixing in some random bytes. RSAREF seeds a random structure with the
     procedure R_RandomUpdate and R_GetRandomBytesNeeded. A random
     structure is considered seeded when the number of bytes still needed
     reaches zero. More bytes can be mixed in after the random structure
     is seeded. A typical application calls R_GetRandomBytesNeeded and
     R_RandomUpdate immediately after calling R_RandomInit.

     RSAREF zeroizes a random structure with the procedure R_RandomFinal.
     A typical application calls R_RandomFinal on exit.


     R_RandomInit

     int R_RandomInit (
       R_RANDOM_STRUCT *randomStruct     /* new random structure */
     );

     R_RandomInit sets up a new random structure.

     Return value:      0     success
                  nonzero     reserved for future compatibility


     R_RandomUpdate

     int R_RandomUpdate (
       R_RANDOM_STRUCT *randomStruct,        /* random structure */
       unsigned char *block,        /* block of values to mix in */
       unsigned int blockLen                  /* length of block */
     );

     R_RandomUpdate mixes blockLen bytes from block into randomStruct.

     Return value:      0     success


                                       52


                     Appendix A: Selected RSAREF Functions


                  nonzero     reserved for future compatibility


     R_GetRandomBytesNeeded

     int R_GetRandomBytesNeeded (
       unsigned int *bytesNeeded,/* number of mix-in bytes needed */
       R_RANDOM_STRUCT *randomStruct         /* random structure */
     );

     R_GetRandomBytesNeeded computes the number of mix-in bytes still
     needed to seed randomStruct, storing the result in bytesNeeded.

     Return value:      0     success
                  nonzero     reserved for future compatibility


     R_RandomFinal

     void R_RandomFinal (
       R_RANDOM_STRUCT *randomStruct         /* random structure */
     );

     R_RandomFinal zeroizes randomStruct.

     No return value.

     R_DigestBlock

     int R_DigestBlock (
       unsigned char *digest,                  /* message digest */
       unsigned int *digestLen,      /* length of message digest */
       unsigned char *content,                        /* content */
       unsigned int contentLen,             /* length of content */
       int digestAlgorithm           /* message-digest algorithm */
     );

     R_DigestBlock computes the message digest of content, storing the
     resulting message digest in digest and its length in bytes in
     digestLen.

     digestAlgorithm is the algorithm with which the content is digested,
     and must be one DA_MD2 or DA_MD5.

     digestLen will not be greater than MAX_DIGEST_LEN.

     Return value:       0    success
       RE_DIGEST_ALGORITHM    digestAlgorithm is invalid








                                       53