Version of 5/14/92 Version of 5/14/92 [Changes from Previous version: GDSS_Sig_Info has been eliminated. Its function is now performed by GDSS_Verify. GDSS_Verify has had is SignatureLen paramater removed. In previous versions signatures were identified by a pointer and a length (because they could contain null bytes). In this version signatures *never* contain null bytes and may therefore be stored in null terminated byte streams. Therefore the SignatureLen parameter has been removed. GDSS_Verify has also had an additional argument added, a pointer to a SigInfo type which contains information on who and when the signature was created (this information was formerly obtained by calling GDSS_Sig_Info. GDSS_Recompose has been added to support Moira (see below for more info). Error codes are now defined in "gdss.h" and more different types of errors are articulated. The "Detailed Inner Workings" section has been edited to make it more accurately reflect what is really going on. -Jeff 5/14/92] The Generic Data Signature Service (GDSS) Motivation As we move administrative applications into the Athena Computing Environment we begin to require a mechanism for maintaining a high level of integrity upon certain information. For the first such application, the Registrar's System, we require a mapping of Kerberos principal name to MIT ID number which is "tamper resistant" against forgery. The traditional approach to providing this form of strong data integrity is to store the information, in the Registrar's case the mapping table, in a secure database on a secure system. The Generic Data Signature Service (GDSS) provides a mechanism that will provide strong integrity checks on information, without the requirement of strong computer security on the database system it is stored in. Overview The GDSS provides the ability to "sign" information in such a fashion that any tampering with that information is detected. By relying on cryptographic techniques the integrity of signed information can be maintained even if it is transmitted across a non-secure, open network such as MITnet. The GDSS system is composed of two key components. The first is a client library of functions that provide the necessary support to application programs so that they may generate digital "signatures" of information and the verification of these signatures. The GDSS client library works in concert with the second key component, the GDSS server system. The GDSS server system is a securely maintained system that is involved in the generation of signatures. Although it requires secure maintenance, it stores none of the data for which it generates signatures. In this fashion it differs from a secure database system, where all secured information must be stored on the secure system. The GDSS secure system needs to protect but one relatively small piece of information, a cryptographic key which is used to generate signatures. The GDSS library provides functions to accept information and generate a digital signature of that information. The digital signature contains information relating the original information along with the Kerberos name of the individual which performed the signature operation and a timestamp. A function is available that accepts information that purports to be signed by a particular signature along with the signature itself. It returns true or false depending on whether the information provided is the same as that which was used to generate the signature. Support routines are provided that return the Kerberos name and timestamp associated with a particular signature. Routine Definitions int GDSS_Sign((unsigned char *) Data, (unsigned int) DataLen, (unsigned char *) Signature) returns 0 on success or an error code on failure. Data: A pointer to the data to be signed (input) DataLen: Length of the data to be signed (input) Signature: A pointer to memory in which the signature is to be stored (output) The signature is a byte stream which is null terminated. GDSS_Sign returns 0 for success or non-zero on error. Errors will be compatible with the compile_et utility and may include errors from the Kerberos library. (Error reporting is as yet not using compile_et. Error codes are defined in "gdss.h" by #define constructs) int GDSS_Verify((unsigned char *)Data, (unsigned int) DataLen, (unsigned char *) Signature, SigInfo *aSigInfo) returns 0 on success or an error code on failure. Data: A pointer to the data to be signed (input) DataLen: Length of the data to be signed (input) Signature: A pointer to the signature to be used for verification (input) A signature is a byte stream which is null terminated. aSigInfo is a pointer to a type "SigInfo" defined in "gdss.h" as: typedef struct { int SigInfoVersion, char pname[KERBEROS_NAME_SIZE], char pinst[KERBEROS_INST_SIZE], char prealm[KERBEROS_REALM_SIZE], t_time timestamp unsigned char *rawsig } SigInfo; GDSS_Verify returns 0 for success, which indicates that signature corresponds to the Data pointed to by the Data pointer and that the Data is the same as was originally presented to GDSS_Sign to create the signature. GDSS_Verify returns non-zero on any internal error or if the Data does not correspond to the signature. Upon a successful return (no error reported) aSigInfo is filled in with the kerberos name and of the original signer along with a timestamp of when the signature was computed. If on input the "rawsig" component was non-NULL, then the raw RSA modulus of the signature is placed here as a null terminated byte stream. This is useful if you wish to take a "SigInfo" structure and recompose it into the original signature. This feature is provided for Moira. Moira will be storing signatures in a context where the kerberos principal of the signer will already be stored in the database. Therefore the copy of it in the non-raw signature is in effect redundant. Because of the size of the Moira database, space is at a premium and this provides a way to save about 100 bytes per Moira database entry. If you do not wish to have the raw signature returned, set aSigInfo.rawsig to NULL. IMPORTANT: If you allocate aSigInfo on the stack MAKE SURE TO EITHER SET aSigInfo.rawsig to NULL or PROVIDE A LEGITIMATE POINTER. If aSigInfo.rawsig contains "stack garbage" the raw signature will be stored into wherever in memory it points. This will most likely cause your program to crash or behave eradically! int GDSS_Sig_Size() No Arguments Returns the length of the memory which should be allocated for storing signatures generated by GDSS_Sign. int GDSS_Recompose (SigInfo *aSigInfo, unsigned char *Signature) aSigInfo: Pointer to a SigInfo type which contains a kerberos name, timestamp *and* a complete "rawsig" raw-signature. (input) Signature: Character string pointer to where the recomposed full signature will be stored. (output) GDSS_Recompose() is primarily intended for the use of Moira which will store signatures in their decomposed "raw" form along with the kerberos information and timestamp info. GDSS_Recompose translates a SigInfo type provided by GDSS_Verify back into the actual full signatures *provided* that the rawsig component is present. Detailed Inner Workings The Client Library GDSS_Sign() performs an MD2 checksum of the Data (whose length is determined from DataLen). It then performs a second MD2 checksum of the MD2 checksum first calculated. It creates a Kerberos "app_req" by calling krb_mk_req specifying the last 32 bits of the second MD2 checksum as the "checksum" argument to krb_mk_req. The first MD2 checksum is placed in a UDP packet along with the resulting KTEXT structure from krb_mk_req() and this information is sent to the Secure Signature Server system. It is retransmitted if necessary until an response is received from the signature server. The signature server will respond with a udp packet containing the resulting signature and signature length. GDSS_Sign then returns this signature. Note: GDSS_Sign sends a UDP packet and blocks awaiting a response. Signatures internally consist of two basic components. The first component contains the information which is returned in the SigInfo type from GDSS_Sig_Info(). The second component is a "digital" signature generated which is a cryptographic function of the Data and the information which is in the first component. The steps to construct the signature will be described later under the description of the Secure Signature Server. GDSS_Verify() performs an MD2 checksum of the input Data. It then takes the input signature and hashes the MD2 checksum along with the Kerberos name information and timestamp that make up the first components of the signature to generate a second MD2 checksum. The second component of the signature is then taken and is RSA "Public Key Encrypted" with the GDSS Public Key (which is embedded in the GDSS library or located via a configuration file). This encryption should yield the same value as the second MD2 checksum described above. If it does, the signature is valid and GDSS_Verify returns successfully. If not either the Data or the supplied signature (or their lengths) is incorrect and nothing can be inferred about the validity of the data. GDSS_Sig_Size() returns the computed length that a signature will be based on the length of the GDSS Public Key. The GDSS Secure Signature Server The Secure Signature Server binds a UDP port upon which is awaits signature requests. Upon receipt of a signature request, it verifies the enclosed KTEXT structure (which contains a Kerberos ticket and authenticator) and makes the appropriate Kerberos calls to determine if the request is authentic. It then builds the first component of the signature based on the information supplied by the Kerberos routines (the authenticated name) and the current time is used to build the timestamp. This information is then combined with the MD2 hash of the Data supplied in the UDP packet to produce a second MD2 hash. This MD2 hash is subjected to an RSA "Private" encryption operation using the GDSS Private Key (which is securely kept secret by the GDSS server) to produce the second component of the signature. Security and Availability issues of the GDSS Service The security of the GDSS service is based on the Security of the GDSS Secure Server system. This server *must* protect the GDSS Private Key from compromise. The GDSS Private Key is the only data which is directly manipulated by the GDSS server. Note: The GDSS Secure server never actually handles any user data. Instead it is only provided with the MD2 checksum of that data. Therefore the operators of the GDSS Secure Server are not in a position to view the data that is signature protected. This implies that it is "safe" to use the GDSS service to protect information that the operators of the system should not be able to peruse! The GDSS_Verify() operation is performed solely on the client system. The Secure Server does not take part in signature verification, and therefore does not need to be operational in order for signature verification to take place. Non availability of the GDSS Secure Server only effects the ability to generate signatures. This is a nice serendipity as we envision that signature verification will be the majority use of the service, and the use most likely to be real-time required. Today the GDSS Secure Server will need to be physically secured, as is the Kerberos server. However because it doesn't store any data, it does not require large amounts of disk space, nor frequent backup (in fact as long as a record of the GDSS Private Key is maintained safely elsewhere, it needs no backup at all!). This greatly leverages our ability to provide for a secure server, as few staff members need to be involved with its maintenance. In the near future, products such as the BBN manufactured "SafeKeyper" (tm) device may permit us to store the GDSS Private Key in tamperproof hardware.