





                                  RIPEM API
                              Library Reference

                            for RIPEM version 3.0

                                Jeff Thompson

                           last modified 10/31/95







  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                12

   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                                17


  APPENDIX A: SELECTED RSAREF FUNCTIONS                     47

   Random Structures                                        47

   R_DigestBlock                                            48











                                      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.

    char *errorMessage;
    unsigned char *partOut, line[256];


                                      4


                  RIPEM API LIBRARY REFERENCE: Introduction


    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.

        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.



                                      5


                  RIPEM API LIBRARY REFERENCE: Introduction



      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;
        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


                                      6


                  RIPEM API LIBRARY REFERENCE: Introduction


        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.
       */
      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



                                      7


                  RIPEM API LIBRARY REFERENCE: Introduction


        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 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):




                                      8


                  RIPEM API LIBRARY REFERENCE: Introduction


  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

  Many RIPEM functions require access to the files in the RIPEM database
  which hold public keys, private keys, etc. When you declare a


                                     10


               RIPEM API LIBRARY REFERENCE: Function Overview


  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.

  Attributes: The RIPEMAttributes Structure



                                     12


               RIPEM API LIBRARY REFERENCE: Function Overview


  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;
    unsigned char signature[MAX_SIGNATURE_LEN];
    int signatureLen;


                                     13


               RIPEM API LIBRARY REFERENCE: Function Overview


  } 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
  DERToCertificate to decode the certificate which contains the
  distinguished name. (See GetDNSmartNameIndex in the next chapter for a


                                     14


               RIPEM API LIBRARY REFERENCE: Function Overview


  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.

  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-


                                     15


               RIPEM API LIBRARY REFERENCE: Function Overview


  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.

  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.

  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.





                                     16





                       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);




                                     17


                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.


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



                                     18


                RIPEM API LIBRARY REFERENCE: Function Details


  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.


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


                                     19


                RIPEM API LIBRARY REFERENCE: Function Details


  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".


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


                                     20


                RIPEM API LIBRARY REFERENCE: Function Details


  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.


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.


                                     21


                RIPEM API LIBRARY REFERENCE: Function Details


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. 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,


                                     22


                RIPEM API LIBRARY REFERENCE: Function Details


  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.


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


                                     23


                RIPEM API LIBRARY REFERENCE: Function Details


  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 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


                                     24


                RIPEM API LIBRARY REFERENCE: Function Details


  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.

  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);


                                     25


                RIPEM API LIBRARY REFERENCE: Function Details


  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.

  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);


                                     26


                RIPEM API LIBRARY REFERENCE: Function Details


  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 and to produce the final part of the output. 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. 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-


                                     27


                RIPEM API LIBRARY REFERENCE: Function Details


  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 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);


                                     28


                RIPEM API LIBRARY REFERENCE: Function Details


  Use this to finalize the processing of a PKCS-compliant message which you
  are deciphering and to produce the final part of the output. 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 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.



                                     29


                RIPEM API LIBRARY REFERENCE: Function Details


  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.
  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.



                                     30


                RIPEM API LIBRARY REFERENCE: Function Details


  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 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.


                                     31


                RIPEM API LIBRARY REFERENCE: Function Details


  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 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;



                                     32


                RIPEM API LIBRARY REFERENCE: Function Details


  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 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.





                                     33


                RIPEM API LIBRARY REFERENCE: Function Details


  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:

  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


                                     34


                RIPEM API LIBRARY REFERENCE: Function Details


  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.

  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


                                     35


                RIPEM API LIBRARY REFERENCE: Function Details


  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.


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.



                                     36


                RIPEM API LIBRARY REFERENCE: Function Details


  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);

  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


                                     37


                RIPEM API LIBRARY REFERENCE: Function Details


  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. 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.


                                     38


                RIPEM API LIBRARY REFERENCE: Function Details


  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.


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.




                                     39


                RIPEM API LIBRARY REFERENCE: Function Details


  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.


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.



                                     40


                RIPEM API LIBRARY REFERENCE: Function Details


  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.

  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);



                                     41


                RIPEM API LIBRARY REFERENCE: Function Details


  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 *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.


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



                                     42


                RIPEM API LIBRARY REFERENCE: Function Details


  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.


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,


                                     43


                RIPEM API LIBRARY REFERENCE: Function Details


  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 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.



                                     44


                RIPEM API LIBRARY REFERENCE: Function Details


  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.

  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);



                                     45


                RIPEM API LIBRARY REFERENCE: Function Details


  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.)

  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.




































                                     46





                 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
               nonzero     reserved for future compatibility


                                     47


                    Appendix A: Selected RSAREF Functions




  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









                                     48