SNP: An Interface for Secure Network Programming Thomas Y.C. Woo, Raghuram Bindignavle, Shaowen Su and Simon S. Lam Department of Computer Sciences The University of Texas at Austin Austin, Texas 78712-1188 Abstract SNP provides a high-level abstraction for secure end-to-end network communications. It supports both stream and datagram semantics with security guarantees (e.g., data origin authenticity, data integrity and data con#dentiality). It is designed to resemble the Berkeley sockets interface so that security can be easily retro#tted into existing socket programs with only minor modi#cations. SNP is built on top of GSS-API, thus making it relatively portable across different authentication mechanisms conforming to GSS-API. SNP hides the details of GSS-API (e.g., credentials and contexts management), the communication sublayer as well as the cryptographic sublayer from the application programmers. It also encapsulates security sensitive information, thus preventing accidental or intentional disclosure by an application program. 1 Introduction The explosive growth of network connectivity has signi#cantly aggra- vated the problem of security. Most existing network programming paradigms adopt a trust-based approach to security (e.g., trusting net- work packets, trusting hosts). This is no longer adequate, especially for malicious attacks. Indeed, with easy access to networks and availability of sophisticated network tools, the effort to mount attacks such as spoof- ing network packets or snif#ng illicit information from network traf#c is substantially reduced. To effectively counter these attacks, a coher- ent security infrastructure is needed. An important element of such a infrastructure is a convenient abstraction for secure application network programming. In recent years, distributed systems security has received a great deal of attention. For example, a number of authentication systems (e.g., Kerberos from MIT [15 ], SPX from DEC [17 ] and KryptoKnight from IBM [9 ]) have been designed and implemented. Although these systems do provide an adequate solution for typical network security concerns, they suffer a major common drawback, namely, it is dif#cult to integrate ____________________________________________________________ Research supported in part by NSA INFOSEC University Research Program under contract no. MDA 904-91-C7046 and MDA 904-93-C4089, and in part by National Science Foundation grant no. NCR-9004464. them into an application. More speci#cally, they do not export a clean and easy-to-use interface that can be readily used in implementing a distributed service. For example, it often takes a considerable amount of effort to #kerberize# an existing distributed service. Besides, the interface provided is not portable, making the switch from one authentication system to another a non-trivial task. The recently published Internet draft standard Generic Security Ser- vice Application Program Interface (GSS-API) [8 ] alleviates the problem somewhat. In fact, both SPX and KryptoKnight1 have already imple- mented a small subset of GSS-API.2 However, the GSS-API interface is still too low-level to be practical for typical network application program- ming. Its proper use requires intimate understanding of the underlying GSS-API concepts, which can cause signi#cant distraction from the main task of a program. It is valid to say that GSS-API is more suited for use in system software than in regular application programming. Indeed, it is intended that a typical caller of GSS-API be a communication protocol, e.g., telnet, ftp [8 , p. 2]. We believe that what is needed is an abstraction for secure net- work programming that can hide most of the details of GSS-API while retaining the same ease of use as most existing abstractions for net- work programming. As an analogy, the raw interface to a protocol (e.g., tcp __input()/tcp __output() for TCP) is often dif#cult to use, whereas programming using a higher-level abstraction (e.g., sockets, TLI) is signi#cantly easier.3 In this paper, we discuss the design and implementation of SNP (Se- cure Network Programming), a high-level abstraction for secure network programming that we have developed. SNP is like sockets or TLI in that it is an interface that provides applications access to network commu- nications. However, it differs from sockets or TLI in many signi#cant ways: - SNP provides secure network communication. For example, it provides data origin authenticity, data integrity and data con#den- tiality services on top of the usual stream and datagram services provided by sockets or TLI. The precise services provided by SNP are detailed in Section 4. - SNP provides an end-to-end communication abstraction at the application level, whereas sockets and TLI are transport level abstractions.4 More speci#cally, a socket represents a transport level endpoint (e.g., a TCP port), while an SNP endpoint repre- sents an application layer entity (e.g., a server). This distinction is important and is further explained in Section 3. SNP is implemented on top of GSS-API. It is currently in the form of a library. It adopts the same basic design as sockets (though several new ____________________________________________________________1 KryptoKnight is not public-domain. The use of GSS-API is mentioned in [9 ]. But it is not clear2to what extent the interface has been implemented. A recent article in comp.protocols.kerberos states that implementations of GSS-API for Kerberos3will also be available. In fact, Berkeley sockets have often been touted as a major contributing factor to the popularity of TCP/IP. Although Berkeley sockets can support a variety of protocols, it was designed4mainly with TCP/IP in mind. This is not strictly true as sockets also provide access to protocols in other layers in the communication hierarchy, cf., raw sockets etc. However, sockets and TLI are typically considered to be transport layer interfaces. ______ * * _ ______________________________________________________________________________________________________________________________* *____________________ ______ #include * * _ ______ ... * * _ ______ if ((snp_ep = snp(AF_INET, SNP_STREAM, SNP_PROTO_DEFAULT)) < 0) - * * _ ______ snp_perror("snp() error :"); exit(1); * * _ ______ " * * _ ______ /* Initialize local and peer addr structs - just as in sockets */ * * _ ______ ... * * _ ______ /* Initialize local & peer name structs as shown below */ * * _ ______ local_name.np.np_val = (char *) malloc(sizeof(client_name)); * * _ ______ local_name.np.np_len = strlen(client_name); * * _ ______ strcpy(local_name.np.np_val, client_name); * * _ ______ peer_name.np.np_val = (char *) malloc(sizeof(server_name)); * * _ ______ * * _ ______ peer_name.np.np_len = strlen(server_name); * * _ ______ strcpy(peer_name.np.np_val, server_name); * * _ ______ * * _ ______ if (snp_attach(snp_ep, &local_name, &peer_name) < 0) - * * _ ______ snp_perror("snp_attach() error :"); exit(1); * * _ ______ " * * _ ______ if (snp_connect(snp_ep, sizeof(struct sockaddr_in), * * _ ______ (struct sockaddr *) (&peer_addr) ) < 0) - * * _ ______ snp_perror("snp_connect() error :"); exit(1); * * _ ______ " * * _ ______ if ((numbytes = snp_write(snp_ep, buf, buf_size)) < 0) - * * _ ______ snp_perror("snp_write() error :"); exit(1); * * _ ______ " * * _ ______ ... * * _ ______ if (snp_close(snp_ep) < 0) - * * _ ______ snp_perror("snp_close() error :"); exit(1); * * _ ______ " * * _ ______ * * _ ______ ... * * _ ______________________________________________________________________________________________________________________________* *_________________ Figure 1: Sample SNP Client Program Fragment calls have been added), which allows easy transitions from socket-based programs. The balance of this paper is organized as follows. In the next section, we present an overview of SNP. This provides a quick introduction to SNP before delving into details in later sections. In Section 3, we elaborate on a list of design requirements and decisions we have made in the design of SNP. In Section 4, we provide a high-level description of the services offered by SNP. In Section 5, we give a speci#cation of the SNP interface. In Section 6, we discuss various considerations that arise in implementing SNP. In Section 7, we provide some #gures on the performance of our implementation. In Section 8, we compare SNP to some related systems. In Section 9, we discuss the lessons learned and directions for future work. 2 Overview of SNP 2.1 A Quick First Look To give a quick introduction of what SNP is, we begin by looking at actual SNP code fragments. Figures 1 and 2 show respectively the typical client and server SNP code. As can be easily seen, the SNP interface closely resembles that of sockets. This resemblance is not a coincidence. Rather, it was a design decision (see Section 3 for the rationale). In fact, most of the calls even retain their familiar semantics from their socket counterparts, though their implementations are quite different. In the following, we will focus only on the calls that are new in SNP. There are two main new calls, namely snp() and snp __attach(). snp() replaces the socket() call in the socket interface. It is similar in functionality to socket() in that it creates a communication endpoint. ______ * * _ ______ * * _ ______________________________________________________________________________________________________________________________* *____________________ ______ #include * * _ ______ ... * * _ ______ if ((snp_ep = snp(AF_INET, SNP_STREAM, SNP_PROTO_DEFAULT)) < 0) - * * _ ______ snp_perror("snp() error :"); exit(1); * * _ ______ * * _ ______ " * * _ ______ /* Initialize local and peer addr structs - just as in sockets */ * * _ ______ ... * * _ ______ /* Initialize local name structs as shown below */ * * _ ______ local_name.np.np_val = (char *) malloc(sizeof(server_name)); * * _ ______ local_name.np.np_len = strlen(server_name); * * _ ______ strcpy(local_name.np.np_val, server_name); * * _ ______ if (snp_attach(snp_ep, &local_name, &peer_name) < 0) - * * _ ______ snp_perror("snp_attach() error :"); exit(1); * * _ ______ " * * _ ______ if (snp_bind(snp_ep, &server_addr, sizeof(server_addr)) < 0) - * * _ ______ snp_perror("snp_bind() error :"); exit(1); * * _ ______ " * * _ ______ if (snp_listen(snp_ep, 5) < 0) - * * _ ______ snp_perror("snp_listen() error :"); exit(1); * * _ ______ " * * _ ______ if ((new_snp_ep = snp_accept(snp_ep, (struct sockaddr *) &peer_addr, * * _ ______ * * _ ______ &addr_len)) < 0) - * * _ ______ snp_perror("snp_accept() error :"); exit(1); * * _ ______ " * * _ ______ if (snp_getpeerid (snp_ep, &client_name) < 0) - * * _ ______ snp_perror("snp_getpeerid() error :"); exit(1); * * _ ______ " * * _ ______ if ((numbytes = snp_read(new_snp_ep, buf, buf_size)) < 0) - * * _ ______ snp_perror("snp_read() error"); exit(1); * * _ ______ " * * _ ______ ... * * _ ______ if (snp_close(new_snp_ep) < 0) - * * _ ______ snp_perror("snp_close() error :"); exit(1); * * _ ______ " * * _ ______ ... * * _ ______ if (snp_close(snp_ep) < 0) - * * _ ______ snp_perror("snp_close() error :"); exit(1); * * _ ______ " * * _ ______ * * _ ______ ... * * _ ______________________________________________________________________________________________________________________________* *_________________ Figure 2: Sample SNP Sequential Server Program Fragment It differs from socket(), however, in that an SNP endpoint corresponds to an application layer entity rather than a transport layer entity. In addition to SOCK __STREAM and SOCK __DGRAM, snp() supports two new kinds of communication semantics: SNP __STREAM and SNP __DGRAM. Both extend the semantics of their respective socket counterparts by adding security guarantees. Speci#cally, an SNP __STREAM connection is authenticated. That is, a connection would be made only if it is accepted by the intended peer (speci#ed by the initiator); and conversely, the iden- tity of the initiator can be uniquely determined by the intended peer once a connection is made. Additional security services (e.g., data integrity, data con#dentiality) can be activated on an SNP __STREAM connection by setting the appropriate options using snp __setopt() (see Section 4). Essentially, an SNP __STREAM connection can be understood as a connec- tion that supports the semantics of a SOCK __STREAM connection even in an environment with intruders.5 The case for SNP __DGRAM is similar. snp __attach() is a completely new call; it does not have a socket counterpart. The main function of this call is to attach an identity to an SNP endpoint. The attached identity is the one that would be authenti- cated to a peer. An identity is not just a name, it is a supported claim of a particular name.6 In other words, an identity can be unambiguously veri#ed to another party. In terms of implementation, an identity con- sists of a name together with a set of credentials that corroborate the ____________________________________________________________5 6 Assuming proper options are set corresponding to the types of threats anticipated. A better term for this is principal. But we intend to keep it informal in this paper and refrain from introducing too many terms. Figure 3: Organization of SNP authenticity of the name. Typically, the operation of snp __attach() involves collecting the appropriate credentials (locally and/or remotely) for supporting the speci#ed name. It should be noted that the identity attached to an SNP endpoint needs not be the identity of the caller per se. For example, a delegate may want to attach its identity as a delegate rather than its own identity. Operationally, a caller is allowed to attach any identity to an endpoint as long as it is able to gather the required credentials to support that identity. 2.2 Components of SNP SNP is designed and implemented in a modular fashion. Each major functionality of SNP is encapsulated in a separate layer that exports a well-de#ned interface. Figure 3 shows the conceptual layering and major components of SNP. SNP-API de#nes the upper (external) interface available to an appli- cation. Internally, SNP makes use of two lower interfaces: GSS-API and a (insecure) network communication API. GSS-API encapsulates the details of the particular authentication protocol used, thus enhancing the independence of the SNP layer from the underlying authentication mechanism. Similarly, the communication API isolates the details of network communication from the SNP layer. We have chosen sockets as the communication API, mainly due to its wide availability. GSS-API in turns makes use of a lower generic cryptographic inter- face. This interface provides access to all cryptographic functions and is generic in the sense that it can support any (symmetric or asymmetric) cryptosystem. This provides #cryptosystem independence# and facil- itates easy substitution when new (implementations of) cryptosystems are available. Further discussion of our GSS-API implementation and the underlying authentication protocol is beyond the scope of this paper; interested readers can consult [23 , 20 ] for more details. The main function of the SNP layer is context and credential man- agement. It initiates the acquisition of credentials, monitors the status of contexts and credentials, and initiates renegotiation (of contexts) and/or reacquisition (of credentials), if necessary. It should be noted, however, that the actual storage of contexts and credentials is internal to GSS-API. 2.3 SNP in Context SNP is part of a larger project of ours that concerns the design and implementation of an authentication framework for distributed systems [21 ]. The framework addresses a range of authentication needs that includes bootstrapping, user logins and peer communications. SNP is designed as an interface for accessing the peer authentication protocol in our framework. Because of its modular design, detailed understanding of the other components in the framework is not required in order to use or understand SNP. Indeed, SNP is relatively independent of the original framework it was designed for, and should be easily portable for use in other authen- tication frameworks (see Section 3). Therefore, we will only brie#y describe the other components in our framework, to the extent they are required for the understanding of SNP. At present, our framework has three protocols: a secure bootstrap pro- tocol that creates a bootstrap certi#cate upon successful bootstrapping; a user-host mutual authentication protocol that creates a login certi#cate when a user successful logs in; and a peer-peer mutual authentication protocol that is the basis for SNP. The login certi#cate is retrieved when a user attaches its identity to an SNP endpoint. This certi#cate is stored in an SNP (GSS-API) credential structure and is used to authenticate the user's identity to its peer. The peer-peer authentication protocol in our framework assumes the use of a commonly trusted authentication server (AS). Apart from its authentication duty, AS is also responsible for generating the session key used in an authentication exchange. Thus, in order for SNP to function correctly, AS needs to be properly set up. For example, it must be properly secured and be given a correct database. The discussion of the associated administrative issues is beyond the scope of this paper. Lastly, a name service is required for translating application layer entities to their transport layer addresses. This name service, however, need not be trusted, as SNP performs the proper authentication during connection establishment. 3 Design Requirements and Decisions In designing SNP, we #rst set out a number of requirements. Based on these requirements, we made several key design decisions. We brie#y discuss the rationale for these requirements and decisions below: - SNP should provide end-to-end communication at the application layer rather than the transport layer. Although the transport layer is the #rst end-to-end layer, we believe the concept of identity is only meaningful at or above the session layer. For example, in Unix and TCP/IP, ports are ephemeral and the association of ports with processes is dynamic.7 We believe it is more appropriate to base our semantics on application level entities than to assume a secure mapping between ports and processes. - SNP should be independent of any particular authentication proto- col or framework. This allows SNP to be portable across different authentication systems. We achieve such independence by using ____________________________________________________________7 Reserved ports are a matter of convention only, there is no permanent binding. Figure 4: Services Provided GSS-API to encapsulate the details of the underlying protocol, and sockets as the communication interface. - It should be easy to convert existing network application programs to use SNP. To achieve this, we designed SNP to retain as much as possible the general structure of a socket program. Hence: (1) only a minimal number of new concepts needs to be learned in order to acquaint oneself with SNP; (2) only minor (mostly syntactic) modi#cations need to be done to convert a socket program to an SNP program, thus signi#cantly facilitating retro#tting. We could have emulated the TLI interface instead. But we believe that sockets and TLI are suf#ciently similar to each other that little extra effort is required to convert TLI programs into SNP programs. Besides, there are far more existing socket programs than TLI programs, though TLI is quickly gaining popularity. - SNP should work in a heterogeneous environment. This entails careful considerations of message encoding and processing. We have chosen XDR [16 ] for this purpose, mainly for its simplicity. ASN.1 [1 ] is used in other authentication systems (e.g., Kerberos, SPX); we #nd it to be overly complicated and not suitable as a prototyping tool. From our experience, XDR has been adequate, though not as #exible as we would like. - SNP should be independent of particular cryptosystems. We achieve this by encapsulating all cryptographic functions using a generic cryptographic interface. In our current implementation, we use the de facto standard cryptosystem trio, i.e., DES [10 ] for symmetric encryption, RSA [13 ] for asymmetric encryption and MD5 [12 ] for message digest. 4 Services Provided Security is only well-de#ned with respect to a threat model. In this paper, we assume the standard threat model. That is, a saboteur can read, insert, delete and modify any network traf#c. It should be noted that a saboteur is not necessarily a totally external intruder, s/he can also be a legitimate user. Thus, s/he can use information available to a legitimate user in mounting an attack. We stress that our model does not include denial of service and traf#c analysis threats. It is always possible for a saboteur to corrupt all packets passing through. Even an in#nitely persistent sender cannot overcome such corruption if the saboteur is equally persistent. Indeed, most network programming abstractions guarantee only safety but not progress. In the following, we present in high-level terms the services provided by SNP. We #rst de#ne below the typical types of services offered by a secure communication connection: - Persistent Delivery (PD) # A sender will persistently try to re- transmit data if it has not been received yet. Thus, PD implicitly assumes the use of acknowledgments. - Best Effort Delivery (BED) # Data sent may or may not arrive at the receiver. Each of the intermediate nodes can either forward or drop the data. - Sequenced Delivery (SD) # If data arrives at a receiver, it must appear in the same order it was sent. That is, no reordering or duplication is allowed. - Data Con#dentiality (DC) # Data is only legible to the intended receiver. - Data Integrity (DI) # Data, if accepted by a receiver, must bear the same content as that sent. - Data Origin Authenticity (DOA) # Data, if accepted by a receiver, must have come from a known speci#c sender. - Data Destination Authenticity (DDA) # When data arrives, a re- ceiver can unambiguously determine that it is the intended receiver. - Connection Authenticity (CA) # A connection, if made, must be between the intended peers. SNP can provide different combinations of the above services. The precise combinations provided is summarized in Figure 4. Each of the two boxes is labeled by a constant denoting the communication semantics, while each of the circles is labeled by an SNP option con- stant. The combination of services provided under a particular commu- nication semantics and set of options is labeled in the intersection of the corresponding regions. For example, under SNP __STREAM, if both SNP ___OPTIONS __SIGNED and SNP __OPTIONS __SEQUENCED are set, the services provided are PD, CA, DI, DOA, DDA and SD. We note that SNP ___OPTIONS __SIGNED and SNP __OPTIONS __ENCRYPTED cannot both be set at the same time. The case for SNP __DGRAM is similar, and is omit- ted. 5 The SNP Interface As with the socket interface, SNP-API functions can be divided into #ve classes: initialization, connection establishment, data transfer, connec- tion release, and utility. We describe the functions in each class below. ______ * * _ ______________________________________________________________________________________________________________________________* *____________________ ______ * * _ ______ Initialization Calls * * _ ______ int snp ( int family, int type, int protocol ); * * _ ______ int snp_bind ( int snp_ep, struct sockaddr *local_addr, int addr_len ); * * _ ______ * * _ ______ int snp_listen ( int snp_ep, int backlog ); * * _ ______ int snp_attach ( int snp_ep, struct name_s *local_name, struct name_s *peer_name ); * * _ ______ * * _ ______ Connection Establishment Calls * * _ ______ int snp_connect ( int snp_ep, struct sockaddr *peer_addr, int peer_addr_len ); * * _ ______ * * _ ______ int snp_accept ( int snp_ep, struct sockaddr *peer_addr, int peer_addr_len ); * * _ ______ Data Transfer Calls * * _ ______ * * _ ______ int snp_write ( int snp_ep, char *buf, int nbytes ); * * _ ______ int snp_read ( int snp_ep, char *buf, int nbytes ); * * _ ______ int snp_send ( int snp_ep, char *buf, int nbytes, int flags ); * * _ ______ * * _ ______ int snp_recv ( int snp_ep, char *buf, int nbytes, int flags ); * * _ ______ int snp_sendto ( int snp_ep, char *buf, int nbytes, int flags, * * _ ______ struct sockaddr *to, int tolen ); * * _ ______ int snp_recvfrom (int snp_ep, char *buf, int nbytes, int flags, * * _ ______ * * _ ______ struct sockaddr *from, int *fromlen ); * * _ ______ Connection Release Calls * * _ ______ * * _ ______ int snp_close ( int snp_ep ); * * _ ______ int snp_shutdown (int snp_ep, int how ); * * _ ______ * * _ ______ Utility Calls * * _ ______ int snp_setopt ( int snp_ep, int level, int optname, char *optval, int optlen ); * * _ ______ * * _ ______ int snp_getpeerid ( int snp_ep, struct name_s *peer_name ); * * _ ______________________________________________________________________________________________________________________________* *_________________ Figure 5: SNP Interface Speci#cation A complete list of all functions is given in Figure 5. Parameter names appearing in the following subsections refer to those shown there. Most functions have semantics similar to their socket counterpart. (In fact, they are given the same names modulo the pre#x #snp __.#) We have not emulated all the data transfer functions of sockets (e.g., sendmsg, recvmsg) due to their intricate semantics. Nonblocking I/O is supported, but asynchronous I/O (i.e., interrupt driven) is not. We also note that most functions below (notable exceptions being the data transfer functions) return 0 on success and 1 on failure. In addition, a global variable snp __errno will contain the appropriate error number on failure. 5.1 Initialization Functions in this class are used for creating and initializing an SNP endpoint. They include snp(), snp __bind(), snp __listen() and snp ___attach(). 5.1.1 snp() snp() creates an endpoint of communication. Its parameters have the same types as socket() and have similar semantics. Currently, the only supported value for family is AF __INET, corresponding to the internet address family. The possible values of type are shown in the following table: ____________________________________________________________________________________ _ _ SNP__STREAM _ Secure Stream _ _ _ _ SNP__DGRAM _ Secure Datagram _ _ _ _ SOCK__STREAM _ Normal (Insecure) Stream _ _ ______SOCK__DGRAM___________________Normal_(Insecure)_Datagram______________________ For protocol, the currently supported values are as follows: _____________________________________________________________________________________________________ _ _ SNP_PROTO_DEFAULT _ Default Authentication Protocol _ _ _ _ SNP_PROTO_PUSH_MODEL _ Push Model Authentication Protocol _ _ _ _ SNP_PROTO_REVERSE _ Reverse Authentication Protocol _ _ _ _ IPPROTO_TCP _ Normal TCP _ _ ______IPPROTO_UDP_______________________________Normal_UDP___________________________________________ A combination of SNP __STREAM and any one of the #rst three protocol values results in a secure equivalent of TCP. Similarly, SNP __DGRAM in combination with one of the #rst three protocol constants provides a secure UDP protocol. The #rst three protocol constants can be used only when the family argument value has been set to either SNP __STREAM or SNP __DGRAM. The use of either IPPROTO __UDP or IPPROTO __TCP results in the normal (i.e., insecure) UDP or TCP protocols, respectively. These are equivalent to the semantics provided by the socket interface. snp() returns an SNP handle, of type int. The handle is an index into an internal table of SNP structures maintained by SNP. Thus, unlike socket(), an SNP handle is not a #le descriptor. Hence, some of the standard functions that apply to a socket descriptor will not apply to an SNP handle. The snp __ep parameter in each of the other functions in Figure 5 refer to an SNP handle obtained from a call to snp(). 5.1.2 snp __bind() After creation, an address may be bound to an SNP endpoint using snp ___bind(). The local __addr and addr __len are of the same types as in the bind() function. They specify the address to be bound. 5.1.3 snp __attach() snp ___attach() is used for specifying the identity a caller wishes to be authenticated as to its peer and the name of the intended peer. The name structure name __s is of the following form: (This structure is automatically generated by rpcgen from a XDR structure.) struct name_s - struct - u_int np_len; /* Length of the name */ char *np_val; /* The actual name */ " np; "; If invoked by a server, peer __name may be set to NULL, in which case connection from any client would be accepted. Once a connection is established, the identity of the client can be discovered by calling snp ___getpeerid() (see below). snp __attach() must be invoked before connection establishment, if secure communication is desired. 5.1.4 snp __listen() The function allows its caller to specify the maximum allowed backlog of connection requests. It has identical semantics as listen(), except it takes an SNP handle. Typically, a caller of snp __listen() is a server. This function can only be used on an SNP __STREAM or SOCK __STREAM connection. 5.2 Connection Establishment The second class of functions consists of snp __connect() and snp __accept(); they are mostly used for stream connections. 5.2.1 snp __connect() For an SNP __STREAM endpoint, this function results in the establishment of a connection with a peer if a corresponding snp __accept() is per- formed by the peer. A successful connection also indicates a successful authentication exchange using the underlying authentication protocol. In the case of SNP __DGRAM, snp __connect() only saves the sup- plied peer address in an internal SNP structure. This address would be assumed to be the destination address in all subsequent data transfer un- less an explicit address is given. No authentication is performed at the time of the call; instead, it is performed at the time of the #rst data transfer call. 5.2.2 snp __accept() snp ___accept() can be used only on an SNP __STREAM or SOCK __STREAM endpoint. It accepts connection requests and completes them if the authenticated peer identity matches the one speci#ed by a previous snp ___attach().8 Successful completion also implies that the peer iden- tity has been authenticated, and can be discovered using snp __getpeerid(). Furthermore, it implies the establishment of a pair of security contexts (one at each peer) and the distribution of a session key. The return value is a new SNP handle which can be used for further communication with the peer. Further connection requests can con- tinue to come in on the original SNP endpoint. If peer __addr and peer __addr __len are non-NULL, they will be #lled in appropriately. 5.3 Data Transfer All of the following data transfer functions return the number of bytes actually sent or received on success and -1 on failure. 5.3.1 snp __sendto() snp ___sendto() sends nbytes of data pointed to by buf to the peer address speci#ed by the to parameter. This function may be used on both stream and datagram endpoints. In case of a datagram endpoint, both to and to __len must be speci#ed. The data will be sent encrypted or signed if the appropriate SNP options have been set (see snp __setopt() below). The possible values and semantics of flags are the same as those in sendto(). 5.3.2 snp __recvfrom() snp ___recvfrom() attempts to receive nbytes of data and stores them in a buffer pointed to by buf. The address and address length of the peer are #lled into from and from __len respectively, if both of them are non-NULL. flags has the same semantics as in the recvfrom(). The incoming data is decrypted or veri#ed, depending upon the SNP options speci#ed. ____________________________________________________________8 If the peer name speci#ed is NULL, connections from any client is accepted. 5.3.3 snp __read(), snp __write(), snp __send() and snp __recv() These calls can only be used on stream endpoints. Their semantics are essentially similar to their socket counterparts. snp __send() and snp ___recv() provides additional features (e.g., such as expedited data) that are not available with snp __write() and snp __read(). The nature of data sent or received depends on the current SNP options. 5.4 Connection Release 5.4.1 snp __shutdown() and snp __close() These functions have similar semantics as their socket counterparts, ex- cept they perform the release only after they have veri#ed that the release request did originate from the correct peer. 5.5 Utility Routines These functions are used for manipulating or retrieving the characteristics of an SNP endpoint. 5.5.1 snp __setopt() snp ___setopt() is used to set options available for a regular socket as well as those speci#c to SNP. A new constant, SNP, has been introduced for the level parameter. The options available at the SNP level are: ___________________________________________________________________________________________________________ _ _ SNP_OPTIONS_DEFAULT _ Reset all option settings to default _ _ _ _ SNP_OPTIONS_ENCRYPTED _ Encrypt all subsequent data _ _ _ _ SNP_OPTIONS_SIGNED _ Sign all subsequent data _ _ _ _ SNP_OPTIONS_SEQUENCED _ Enforce sequencing on data _ _ _ _ SNP_OPTIONS_NOTIFY _ Notify caller on context expiry # do _ _ _ _ _ not reinitiate authentication _ _ ______SNP_OPTIONS_CONTEXT_TIME_____________________Set_context_expiration_time______________________________ Setting SNP __OPTIONS __DEFAULT results in resetting all options to their default settings; that is, no encryption, no signing and no sequencing. Setting SNP __OPTIONS __ENCRYPTED causes subsequent outgoing data to be encrypted. Setting SNP ___OPTIONS __SIGNED causes subsequent outgoing data to be signed. The key to be used for encryption and signing is the session key main- tained in the current security context. Options SNP __OPTIONS __ENCRYPTED and SNP __OPTIONS __SIGNED cannot be set at the same time. To enforce sequencing of data, option SNP __OPTIONS __SEQUENCED should be set. This may be used in conjunction with either SNP ___OPTIONS __ENCRYPTED or SNP ___OPTIONS __SIGNED. When the current security context expires, the SNP layer automat- ically renegotiates a new context. This can be disabled by setting SNP ___OPTIONS __NOTIFY; in which case, the SNP user will be noti#ed of context expiry when it performs an SNP call. The duration of a context can be set using the SNP __OPTIONS __CONTEXT __TIME option. Note that the #rst #ve options are toggle #ags, while the last one requires the context duration to be speci#ed in optval. ______________________________________________________________________________________________________________________________* *____________________ ______ Connection Establishment * * _ ______ * * _ ______ (CE1) I : generate nonce nI * * _ ______ (CE2) I ! R : I ; nI * * _ ______ * * _ ______ (CE3) R : generate nonce nR * * _ ______ (CE4) R ! AS : I ; nI ; R; nR * * _ ______ * * _ ______ (CE5) AS : generate key k * * _ ______ (CE6) AS ! R : ffI ; nI ; R; nR ; k g 1 g* *kR _ ______ kAS * * _ ______ (CE7) R ! I : ffI ; nI ; R; nR ; k gk1 g* *kI ,fnI ; nR _ gk ______ AS * * _ ______ (CE8) I ! R : fnR gk * * _ ______ * * _ ______ Connection Release * * _ ______ * * _ ______ (CR1) I ! R : fI ; nI ; R; nR gk * * _ ______ (CR2) R ! I : fnI ; nR gk * * _ ______________________________________________________________________________________________________________________________* *_________________ Figure 6: Underlying Authentication Protocol 5.5.2 snp __perror() and snp __getpeerid() snp ___perror() performs the same function as the standard perror() function, except that it accounts for SNP-API error codes as well. snp __getpeerid() retrieves the authenticated identity of the peer.9 6 Overview of Implementation To facilitate discussion of SNP's implementation, it is helpful to #rst brie#y describe our implementation of GSS-API. The authentication pro- tocol underlying our GSS-API implementation is shown in Figure 6 (I denotes the initiator, R the responder and AS the authentication server). The protocol was initially published in [22 ], and later veri#ed in [20 , 23 ]. The mapping of this protocol to GSS-API is quite straightforward, and is described in [23 ]. The key point to note is that the communications with AS (steps (CE4)#(CE6)) are completely encapsulated within GSS-API, and are not observable by the SNP layer. Typically, an SNP-API call is translated into a number of GSS-API calls together with calls to the communication layer. GSS-API is respon- sible for generating tokens that are to be shipped using the communication layer. In simple terms, the main responsibility of the SNP layer is to re- quest the right tokens to be generated (according to user request and current state) and to ensure they are properly transferred to the peer SNP layer. Figure 7 shows the relationship between SNP-API calls and GSS-API calls. (The bold arrows in Figure 7 correspond to the protocol steps in Figure 6.) For example, a call to snp __connect() results in two calls to gss __init __sec __context() as well as three calls to the communication layer. There are several major considerations in implementing SNP. We describe them below: - Two types of messages, namely, data and control, are transferred between SNP peers. Data messages contain user data and corre- spond to SNP data transfer calls, while control messages contain information related to the operation of the SNP layer (e.g., con- nection establishment request/response) and correspond to SNP ____________________________________________________________9 In fact, the unauthenticated identity of the peer is available as soon as the underlying authentication protocol has proceeded beyond a certain point, even if the authentication exchange fails at the end. Figure 7: Control and Data Flow control calls (e.g., snp __connect()) and functions (e.g., context renegotiation). There are two ways these messages can be transferred. One is to multiplex them onto a single connection, and the other is to create dedicated connections for each type of messages. We opted for the latter because control messages should generally be given priority over data messages. Thus, if they are to be transferred on the same connection, the underlying communication mechanism must support some form of priority message facility. Most existing communication mechanisms (sockets in particular) do not support such priority message processing well.10 The two-connections solution avoids the dependence on such a mechanism.11 - The two-connections solution also simpli#es buffering concerns. Speci#cally, by always reading from the control connection (and 10____________________________________________________________ TCP does not support out-of-band data. It does support some elementary form of urgent data with the urgent bit and the urgent pointer. Berkeley socket supports out-of-band data, though1the1precise semantic guarantee is highly implementation-dependent. In some sense, this is arguable because typically, there is no guarantee on the relative arrival times of messages sent on different connections. However, in practice, for connec- tions with the same source and destination, the times of arrival closely follow the times of the respective sends. Figure 8: Data Structures ___________________________________________________________________________________________________________________________________* *_______________ ______ struct snp_structin-t control_sockfd; /* Control socket desc * * */_ ______ int data_sockfd; /* Data socket desc * * _*/ ______ int family; /* Params specified in ca* *ll */_ ______ * * _ ______ int type; * * _ ______ int protocol; * * _ ______ struct sockaddr *local_addr; /* Obtained from snp_bind() */ * * _ ______ int local_addr_len; * * _ ______ struct sockaddr *peer_addr; /* Obtained from snp_connect */ * * _ ______ int peer_addr_len; /* or first data xfer calls * * */ _ ______ struct name_s *local_name; /* Obtained from snp_attach() */ * * _ ______ struct name_s *peer_name; * * _ ______ gss_cred_id_t cred_list_ptr; /* Credentials pointer */ * * _ ______ gss_ctx_id_t ctx_list_ptr; /* Context pointer * * */ _ ______ int secure_options; /* Obtained from snp_setopt() ** */ _ ______ int no_send; /* Options for snp_shutdown* *() */ _ ______ int no_recv; * * _ ______ struct msg_s *remaining_data; /* Data recd but not requested */ * * _ ______ a_uint16 seq_number; /* For the GSS sequencing * * */ _ ______ a_uint16 recd_seq_number; * * _ ______ * * _ ________"__________________________________________________________________________________________________________________________* *____________ Figure 9: SNP Structure responding to it) #rst, we no longer need to buffer all the user data preceding a control message if a control action is needed. The elimination of extra buffering also improves performance. - The use of two connections raises the question of the address to which the second connection should be bound. Our current imple- mentation always establishes the second (i.e., control) connection at a #xed offset from the user supplied (i.e., data) connection address. If adopted as a convention, this should not create any collision problem. The main data structure in the SNP layer is the snp __struct struc- ture. Its de#nition is shown in Figure 9. The control __sockfd and data __sockfd #elds contain, respectively, the socket descriptors for the control and data connections. The #elds cred __list __ptr and ctx ___list __ptr contain pointers to GSS layer structures (see Figure 8). The meanings of most other #elds are given in the comments. Each call to snp() creates an snp __struct structure; the SNP handle returned is an index into an internal table of pointers to snp __struct maintained by the SNP layer. We have only touched upon the main ideas in our implementation. Most of the details concerning context expiration, context renegotiation, etc., have been omitted due to length limitation. This paper is intended only as a preliminary overview. We hope to provide a full account in a #nal report. ______________________________________________________________________________________________________________________________* *__________________________@ ___________Data_Length________________16B_________512B____________1KB____________2KB____________4KB____________8KB__________16* *KB___________32KB_________@ ___________DES_Encryption___________0.42___________2.85___________5.25___________9.97_________19.15__________37.47__________75* *.19_________152.58________@ ___________DES_Decryption___________0.41___________2.94___________5.40__________10.19_________19.54__________38.20__________77* *.24_________158.78________@ ___________DES_Sign_________________0_.36__________0.54___________0.73___________1.14___________1.89___________3.46___________* *6.61_________12.72________@ ___________DES_Verify_______________0_.33__________0.54___________0.73___________1.11___________1.89___________3.42___________* *6.57_________12.75________@ ___________RSA_512_Encryption____541.24_________542.21_________546.00_________551.92_________560.44_________577.69_________618* *.54_________689.76________@ ___________RSA_512_Decryption______53.93__________56.85__________59.11__________63.83_________73.14__________91.29_________127* *.49_________198.42________@ ___________RSA_512_Sign__________54_0.25________540.10_________540.45_________540.64_________544.82_________544.01_________550* *.25_________551.17________@ ___________RSA_512_Verify___________53.86_________53.82__________54.20__________54.49_________55.48__________57.06__________60* *.10__________66.21________@ ___________MD5_____________________0.0_56__________0.25___________0.43___________0.79___________1.52___________3.00___________* *6.00_________12.11________@ Table 1: Cryptographic Performance (in milliseconds) 7 Performance In this section, we present some performance results of our SNP imple- mentation. The measurements were done on a network of Sun SPARC- stations 10/30 running SunOS 4.1.3. The resolution of the system clock is in the order of microseconds.12 We #rst calibrate the performance of our cryptographic packages. Our DES package is a generic public domain one, while our RSA/MD5 package is from RSAREF [4 ]. Both packages are relatively portable, and are not optimized. The calibration allows us to determine the overhead introduced by the SNP layer, excluding cryptographic cost. This provides a better measure of the performance of our SNP implementation, because as more highly optimized cryptographic packages and hardware become available, the cryptographic cost will diminish, while the SNP overhead remains constant. Referring to Table 1, the following observations can be made: (1) The performance of both DES (CBC mode) and MD5 is linear with respect to data size. (2) The performance of RSA is also linear except for small data sizes. This is due to the fact that for large data sizes, the RSA implementation does not perform #true# RSA encryption. Instead, it #rst generates a random DES key, then encrypts the data with the DES key, and #nally encrypts the DES key using RSA. Our measurements of SNP performance are given in Tables 2 and 3. All measurements are for SNP __STREAM; similar measurements apply to 12____________________________________________________________ The measurement error, however, is much worse because of context switching, function call overhead, etc. _______________________________________________________________________________________________________________________________ ______ _ ____________Connect________________________________Close__________________ ______ _ Number of_ _ Subtotal _ _ Number of _ Subtotal _ _ ______________________________________________Operation_s__________________________Operations__________________________________ ___________RSA____________Encry_ption_____________2@1kB___________1092.00______________________________________________________ ______ _ ___Decryptio_n_____________2@1kB_____________118.22____________________________________________ ______ _ ___Sign____________________1@1kB_____________540.45____________________________________________ __________________________Verify__________________2@1kB_____________108.40_____________________________________________________ ___________DES____________Encryp_tion___________2@200B_________________2.85_________2@200B_________________2.85________________ ______ _ ___Decryptio_n___________1@200B_________________1.45_________2@200B_________________2.90_______ __________________________Key_Gen.________________________1____________0.38____________________________________________________ ___________XDR____________Encod_e______________21@600B________________29.82_________2@200B_________________2.84________________ __________________________Decode_______________20@600B_________________2.00_________2@200B_________________0.20________________ ___________Total_Time_____________________________________________1895.57__________________________________8.79________________ ___________Measured_Time__________________________________________2148.40________________________________24.90_________________ ___________SNP_Overhead_____________________________________________252.83_______________________________16.11_________________ ___________Socket_Time_________________________________________________2.40________________________________0.40________________ Table_2:__Connect_and_Close_Calls_(in_milliseconds)_____________________________________________ ____________Buffer_Length1kB_________2kB_________4kB________8kB_________16kB___________________ ______ _ Socket 0.6 __ 1.0 _ 1.3 _ 2.8 _ 5.2 _ _ _________SOCK_STREAM__________________________________________________________________________ ______ _ SNP 0.7 __ 1.1 _ 1.5 _ 3.4 _ 6.7 _ _ _________SOCK_STREAM__________________________________________________________________________ ______ _ SNP 0.1 __ 0.1 _ 0.2 _ 0.6 _ 1.5 _ _ _________Overhead_____________________________________________________________________________ ______ _ SNP_STREAM 2.2 __ 3.2 _ 4.4 _ 7.1 _ 12.6 _ _ _________Plain________________________________________________________________________________ ______ _ SNP_STREAM 4.2 __ 5.8 _ 8.8 _ 14.6 _ 27.1 _ _ _________Signed_______________________________________________________________________________ ______ _ SNP_STREAM 13.0 _ _ 22.9 _ 42.7 _ 82.8 _ 163.6 _ _ _________Encrypted____________________________________________________________________________ Table 3: Data Transfer Calls (in milliseconds) SNP ___DGRAM, and are omitted. Note also that these measurements are based on the use of 512-bit RSA keys (i.e., modulus).13 Table 2 shows the timing results for connection establishment (i.e., snp ___connect()/snp __accept()) and release (i.e., snp __close()). The Total Time row gives the amount of time accounted for by crypto- graphic and XDR operations. The Measured Time row gives the observed times in establishing and closing an SNP connection. The difference be- tween Measured Time and Total Time (the SNP Overhead row) gives the overhead introduced by SNP. The Regular Socket row gives the time it takes for the corresponding socket calls to complete. Thus, for connection establishment, SNP introduced around 0.2s overhead. A major compo- nent of this overhead is the extra round-trip delay for the communication with the authentication server and the associated message processing at the authentication server. For connection release, the SNP overhead is around 16ms. Table 3 shows the timing results for data transfer calls (speci#cally for snp ___write()). The #rst two rows give the times for a SNP __STREAM connection with the encrypt and sign options set, respectively. The third row gives the time for a regular SNP __STREAM with no option set, whereas the fourth row gives the time using plain sockets. The SNP Overhead row gives the overhead introduced by SNP. It can be observed that the SNP overhead is minimal. Two conclusions can be drawn from these measurements: (1) The cost of cryptographic operations dominates the total cost of SNP. We believe this can be generalized to any cryptographic security mechanism. (2) It is possible to provide security at the application layer without incurring 13____________________________________________________________ Our implementation is parametric with respect to key length. We can easily switch over to 1024-bit keys. That, however, will slow things down signi#cantly. The increase in cost is not linear in key length. undue overhead, even with an unoptimized implementation. We expect a streamlined implementation to perform even better. 8 Related Work Most existing work on secure network communication is focused on the protocol or architecture aspects [3 , 9 , 15 , 17 ]; not much has been done concerning a general secure application network programming interface. The work most relevant to ours includes several secure RPC systems: the secure RPC package in [2 ], Sun secure RPC [18 ] and DCE secure RPC [14 ]. The goals of these systems are similar to ours: to provide applications transparent access to secure communication. However, the models of communication adopted are different. RPC assumes an im- plicit communication model. That is, its users do not directly manage communications, but instead they deal with high-level abstractions in terms of procedures. SNP assumes an explicit communication model; SNP users are directly responsible for initiating connections, sending and receiving data, and closing connections. The same difference exists between sockets/TLI and RPC styles of network programming. Apart from this, the implementation of these RPC systems is totally different from ours. For example, they tend to be tightly coupled to the underlying protocol (e.g., a modi#ed Needham-Schroeder protocol [11 ] is used in [2 ], Kerberos is used in DCE). Our use of GSS-API provides protocol independence. A recent paper by Wobber et al. [19 ] describes an operating system interface for supporting authentication. The interface is based on a formal theory of a speaks for relation [7 ]. Its concrete implementation contains several interesting abstract datatypes, e.g., a Prin type that represents principals, and an Auth type that represents principals a process can speak for. In relating to our work, their interface can be used as an alternate lower interface for SNP. In other words, instead of translating SNP-API calls to GSS-API calls, they can be translated to calls to the interface in [19 ]. Such a translation should be quite straightforward because of the high level of abstraction supported. A major disadvantage of their interface, though, is the lack of compatibility with other security mechanisms, e.g., Kerberos. Moreover, their interface has only been implemented on the Taos operating system, and is currently not available on Unix. 9 Discussion and Future Work We believe SNP represents an important #rst step toward secure network programming for the masses. It is clear that many important issues need to be resolved before this could be a reality. Some of these issues are: the development of a security infrastructure that provides uniform management and distribution of credentials (particularly for interdomain authentication), and operating system support for basic security concepts such as identity (see [19 ]). One of the other impediments is performance. With rapidly improv- ing cryptographic software and hardware, this should be a diminishing problem. As demonstrated in [5 ], the speed of a modern RISC-based workstation is already quite adequate for most cryptographic computa- tion, provided the right algorithms and optimizations are used. We are also considering several interesting extensions to the SNP interface. First, delegation can be added. This would involve the ad- dition of two new calls: snp __delegate() for the delegating process and snp __assume() for the delegate. Delegation allows a delegate to act with the same authority as the delegating process. Second, the snp ___attach() call can be extended to accept identity expressions in- stead of just simple identity speci#cations. An identity expression can specify a combination of identities that would be communicated to the peer. In terms of implementation, we may try to port SNP to other authenti- cation systems conforming to GSS-API. Also, the essential ideas of SNP can be adapted to provide security at other layers (e.g., transport). The lessons we learned in designing and implementing SNP provide useful references in such an effort. Concerning the design of our interface, we have made the compat- ibility with sockets as one of our top design requirements. With our present design, a typical socket program can be converted into an SNP program by simply adding an snp __attach() call,14 without signi#- cantly modifying any of the existing code. Alternate designs with less compatibility are possible. For example, the concept of identity can be promoted to ##rst class citizen# status, replacing completely the use of socket addresses. For example, the functions snp __connect() and snp ___accept() would then become int snp_connect ( int snp_ep, struct name_s *peer_name ); int snp_accept ( int snp_ep, struct name_s *peer_name ); Another concern in the interface design is user control. How much control should a user be given and how should it be done? For example, users (with the help of an operating system) may wish to explicitly manage credentials themselves, or to use their own encryption keys or algorithms. Our present design allows very limited user control (mainly through snp ___setopt()); this could be appropriately extended. Finally, there is the question of what the best layer for providing security support for network communication is. It can be argued that there is no single best layer for this purpose. The question then becomes: what is the best placement of security functionalities into different layers so that the resulting architecture is most general and admits least duplication? Much more research is needed to obtain an answer. Acknowledgments We wish to thank our shepherd Adam Moskowtiz and the anonymous referees for their constructive comments and suggestions. We are also grateful to Dinesh Das and members of the Network Research Seminar of the University of Texas at Austin for listening to our ideas. References [1] CCITT Recommendation X.208 Speci#cation of Abstract Syntax Notation one (ASN.1), 1988. See also ISO/IEC 8824, 1989. 14____________________________________________________________ And also pre#xing socket calls with #snp__#. [2] A.D. Birrell. Secure communication using remote procedure calls. ACM Transactions on Computer Systems, 3(1):1#14, February 1985. [3] J. Ioannidis and M. Blaze. The architecture and implementation of network- layer security under unix. In Proceedings of 4th Usenix Unix Security Workshop, Santa Clara, California, October 4#6 1993. [4] RSA Laboratories. RSAREF: A cryptographic toolkit for privacy-enhanced mail. January 5 1993. [5] J.B. Lacy, D.P. Mitchell, and W.M. Schell. Cryptolib: Cryptography in software. In Proceedings of Usenix Unix Security Workshop IV, pages 1#17, Santa Clara, California, October 4#6 1993. [6] B. Lampson, M. Abadi, M. Burrows, and T. Wobber. Authentication in distributed systems: Theory and practice. In Proceedings of 13th ACM Symposium on Operating Systems Principles, pages 165#182, Asilomar Conference Center, Paci#c Grove, California, October 13#16 1991. [7] B. Lampson, M. Abadi, M. Burrows, and T. Wobber. Authentication in distributed systems: Theory and practice. ACM Transactions on Computer Systems, 10(4):265#310, November 1992. A preliminary version of this paper appeared as [6 ]. [8] J. Linn. Generic Security Service Application Program Interface, September 1993. RFC 1508. [9] R. Molva, G. Tsudik, E. Van Herreweghen, and S. Zatti. KryptoKnight authentication and key distribution system. In Proceedings of 2nd European Symposium on Research in Computer Security, pages 155#174, Toulouse, France, November 23#25 1992. Springer Verlag. [10] National Bureau of Standards, Washingtion, D.C. Data Encryption Stan- darad FIPS Pub 46, January 15 1977. [11] R.M. Needham and M.D. Schroeder. Using encryption for authentication in large networks of computers. Communications of the ACM, 21(12):993# 999, December 1978. [12] R. Rivest. The MD5 Message-Digest Algorithm, April 1992. RFC 1321. [13] R.L. Rivest, A. Shamir, and L. Adleman. A method for obtaining digital signatures and public-key cryptosystems. Communications of the ACM, 21(2):120#126, February 1978. [14] W. Rosenberry, D. Kenny, and G. Fisher. Understanding DCE. O'Reilley & Associates, Inc., 1992. [15] J.G. Steiner, C. Neuman, and J.I. Schiller. Kerberos: An authentication service for open network systems. In Proceedings of USENIX Winter Con- ference, pages 191#202, Dallas, TX, February 1988. [16] Sun Microsystems, Inc. XDR: External Data Representation Standard, June 1987. RFC 1057. [17] J.J. Tardo and K. Alagappan. SPX: Global authentication using public key certi#cates. In Proceedings of 12th IEEE Symposium on Research in Security and Privacy, pages 232#244, Oakland, California, May 20#22 1991. [18] B. Taylor and D. Goldberg. Secure networking in the Sun environment. In Proceedings of Summer Usenix Conference, pages 28#37, Atlanta, Georgia, June 1986. [19] E. Wobber, M. Abadi, M. Burrows, and B. Lampson. Authentication in the Taos operating system. In Proceedings of 14th ACM Symposium on Operating Systems Principles, Ashville, North Carolina, 1993. [20] T.Y.C. Woo. Authentication and Authorization in Distributed Systems. PhD thesis, Department of Computer Sciences, The University of Texas at Austin, May 1994. [21] T.Y.C. Woo and S.S. Lam. Authentication for distributed systems. Com- puter, 25(1):39#52, January 1992. [22] T.Y.C. Woo and S.S. Lam. #Authentication# revisited. Computer, 25(3):10, March 1992. [23] T.Y.C. Woo and S.S. Lam. Design, veri#cation, and implementation of an authentication protocol. Technical Report TR 93-31, Department of Computer Sciences, The University of Texas at Austin, November 1993. Thomas Y.C. Woo is a Ph.D. candidate at the Department of Computer Sciences at the University of Texas at Austin. His research interests include computer networking, distributed systems, security and multime- dia. Thomas received a BS (First-Class Honors) degree in computer sci- ence from the University of Hong Kong and an MS degree in computer science from the University of Texas at Austin. Raghuram Bindignavle is a Masters candidate at the Department of Computer Sciences at the University of Texas at Austin. He received his BE in computer science at the Regional Engineering College of the University of Allahabad, India. Shaowen Su is a graduate student at the Department of Computer Sci- ences at the University of Texas at Austin. He received his MS in Engineering Physics from the University of Oklahoma and his BS in Physics from Beijing University, Beijing, P.R.China. Simon S. Lam is chairman of the Department of Computer Sciences, University of Texas at Austin, and holds two endowed professorships. Prior to joining the University of Texas at Austin faculty in 1977, he was a research staff member at the IBM T.J. Watson Research Center, Yorktown Heights, New York from 1974 to 1977. His research interests are in the areas of computer networks, communication protocols, performance evaluation, formal veri#cation, and network security. Simon received the BSEE degree with Distinction from Washington State University in 1969, and the MS and Ph.D. degrees in engineering from the University of California at Los Angeles in 1970 and 1974, respectively. He was a recipient of the 1975 Leonard G. Abraham Prize Paper Award from the IEEE Communications Society. He organized and was program chair of the #rst ACM SIGCOMM Symposium held at the University of Texas at Austin in 1983. He presently serves on the editorial boards of IEEE Transactions on Software Engineering and IEEE/ACM Transactions on Networking.