Previous Next Contents

2   Authentication

Authentication takes place in two phases. First, the application which is injecting PLAN programs into the network must authenticate itself with the node on which it requires secure access. This is done by an exchange of messages (a protocol) which allows both parties to derive a shared secret.

Secondly, after the authentication protocol has taken place, the user uses the shared secret to sign a chunk (a piece of code, see below) to be executed on the secured node. Before evaluating the chunk, the node verifies the signature (using the derived shared secret) as that of the purported identity of the code. If this checks out, then that code is evaluated with the privilege of that user -- any additional services that are allowed to the represented user are added to the default environment, and any restricted services are subtracted. This technique is called namespace-based security. We'll talk about it first, and then discuss authentication.

2.1   Autheval

A chunk (or code hunk) may be thought of as a function that is waiting to be applied. In PLAN, chunks are first-class (that is, they are program values), and consist internally of some PLAN code (if necessary), a function name, and a list of values to be used as arguments during the application. A chunk is evaluated as part of normal packet evaluation, or by passing it to the eval service, which processes the definitions in the code, resolves the function name with the current environment, performs the application, and returns the result.

We've added an additional service called authEval which takes as arguments a chunk and a digital signature. The chunk is in marshalled form, represented as a PLAN value of type blob (meaning unstructured data), while the signature is a PLAN three-tuple of type blob × int × int. The first field of the three-tuple is the cryptographic signature, the second is the user's identifying SPI (Security Parameters Index) as negotiated in the authentication protocol, and the final field is a counter, to prevent replay attacks. authEval verifies the digital signature against the binary representation of the chunk. If successful, the chunk is evaluated as in eval; otherwise, the exception AuthenticationFailed is raised. During the evaluation of the chunk, the authentication code keeps track of the authenticated principal by associating it with the current thread id. When the chunk terminates, the association is removed. This way, if any service invoked as a result of chunk evaluation wishes to perform authorization, it may ask the authentication service for the current principal. Because a caller's thread id cannot be forged, this provides a safe way to track a principal without worry that some malicious service will change the associated principal after the authentication phase.

2.2   Authentication protocol

As mentioned, authentication is done in advance of any PLAN program exchange through a protocol which negotiates a secret shared between a user's application and a PLAN node. This protocol is a variant of the well-known Diffie-Hellman [4] exchange which makes use of public key cryptography to prevent man-in-the-middle attacks. We illustrate this protocol with an exchange initiated by an application to a node:

  1. The user application requests authentication with a remote PLAN node. The application sends a PLAN program to the node with which it wants to authenticate. This program invokes the PLAN service DHmessageOne with two arguments: a certificate, and a signature of that certificate using the user's public/private key pair. In the current implementation, we use DSA [5] keys for authentication. All certificates used during the exchange are PLAN tuples which begin with the following four fields: The latter three fields are are essentially used to prevent replay attacks. Note that the duration of time fields implies the level of synchronization between the two nodes' clocks. This first certificate additionally contains: The exchange id is generated at the sender, and is used to identify this particular protocol exchange. At the completion of the protocol it will be used to establish the SPI (more later). For a node, the address is represented as a PLAN host, while an application's address is of type host × port.

  2. When the node receives the message, it verifies the signature on the certificate with the certificate signer's key. It then makes sure the certificate is active and has not expired, and that the receiver's address is the current node.

    If the current node wishes to negotiate with the sender, it creates a bit of state to keep track of the exchange. It stores the sender's exchange id, calculates its own exchange id, and additionally stores the sender's address and public key. It also calculates its local portion of the shared secret and the ``public'' value of this secret. The response certificate includes both exchange id's, the public value, the app's public key, and both addresses.

    Since the response message is being sent to an application, rather than a node, it is packaged as a tuple, labeled by a string ``DHmessageTwo,'' to be delivered to the application. This tuple also contains the certificate and its signature.

  3. The application verifies the signature, looks up the exchange id to find the information stored about this exchange, and verifies that it is all correct. It then calculates its secret and corresponding public value, then combines it with the value in the message to produce the shared secret. The SPI identifying the secret is then calculated based on the two exchange id's. This SPI is used to identify the secret in later messages which have been signed using the secret. The application's public value is included in a message back to the node which is essentially a mirror of the message just received. As described earlier, this third message is actually a PLAN program that invokes the PLAN service DHmessageThree with two arguments: the certificate containing the described information, and a signature of that certificate using the user's public/private key pair.

  4. The node receives the final message and repeats the actions taken by the application for the previous message. No response is sent; the protocol is complete.
Each principal in the exchange now has secret known only to the other principal. The shared secret is used to sign chunks used by authEval, as described. This is done by a technique called HMAC-SHA1 [6]. At this point you might be wondering, why go through all this trouble when we can simply sign and verify chunks using a principal's public key? The simple answer is that using HMAC-SHA1 is much faster, following the trend that secret cryptography is faster than public key cryptography.

In our implementation, the secret is stored in two tables; one table indexed by the peer's address (which includes other information about the protocol), and another indexed by SPI. The former table may be used by the application when it wants to send a message to the peer, the latter table is used to look up the SPI found in a signed chunk so that the signature may be verified. More about how the implementation may be used is found in the next section.

Note that this authentication exchange is not limited to application contacting a node---nodes may contact other nodes and applications may contact other applications. The latter is in common practice in the Internet today, and the former may be used, for example, to establish trust relations between administrative domains.

2.3   Authentication Tutorial

We'll present two tutorials for authentication in PLAN, presenting first application-node authentication, and then node-node authentication. For these tutorials to work, you need to compile PLAN to use QCM, and to have authentication/authorization debugging messages turned on. To do so, edit the Makefile as follows:
  1. Change the line at the top of the Makefile
    # USE_QCM = true             ### Use QCM as the node policy manager
    
    to be uncommented:
    USE_QCM = true             ### Use QCM as the node policy manager
    
  2. Add the AUTH_DEBUG flag to the compilation flags. That is, change
    CAMLP4FLAGS     = -DPLAN_UNSAFE -DPLANPORT_TCP
    
    to be
    CAMLP4FLAGS     = -DPLAN_UNSAFE -DAUTH_DEBUG -DPLANPORT_TCP
    
    After making these changes, do a make clean depend to regenerate the dependencies, and then make to rebuild the system.

2.3.1   Application-Node Authentication

A sample application and service have been provided as templates for writing code using the security services. This application will perform the authentication protocol as described and then invoke a user-defined function which may make use of the shared secret; by default this involves simply printing a message from the authenticated node.

To try out the application, first start an active router as described in the PLAN tutorial [3]. As in the tutorial, we'll assume that the router is started on a machine with address m on port 3324. Using this port is important because the router will use the keys provided with the plan distribution in the file key.3324. We also assume that all commands are invoked from the plan directory.

We need to start the application so it may negotiate with the router. The application is called authapp; it may be built by doing
make authapp
which deposits the executable in the bin directory. Usage of the program can be obtained by bin/authapp -help:
usage: bin/authapp [-hf host_file] [-p local_port] [-s socknum]
      [-server] dest_host key_file
  -hf Specify name resolution file (default=EXP_IP_ADDRS).
  -p Set injection port. (default=None)
  -server Set Server Mode (default=not set)
  -s Specify domain socket to connect to on receiving side. (default=3324)
As required arguments, the application takes the hostname of the router with which it wishes to communicate and the files that store its own public and private keys. Keys can be generated by another program provided with the distribution called plan_keygen, which may be built using the command make plan_keygen. This program takes a filename argument and generates DSA public and private keys storing a base-64 representation in the given file.

Standard options to authapp include -hf for the name of the file used to resolve domain-name addresses, and -p for the port number of the local plan interpreter; see the tutorial document for more on these. authapp is general enough that it may authenticate not only with an active node, but also with another application (and may itself be the server in that instance) or serve as the server side of a node-app authentication. In the case that the application is authenticating with another application, authapp would use the -s option to specify the PLAN port number on which the remote application is listening at the specified destination. To use authapp as the receiving participant in the protocol exchange (where the initiator is either another authapp or a PLAN router), use the -server option.

In our example, we'll assume that we're starting the authapp on the same machine as our router, m. From another xterm, we would type

bin/authapp -p 3324 m key

where key is the file that contains the application's public and private keys. This has been provided with the distribution (you don't need to generate it) in the plan directory.

The application sends the first message to the node. Printed on the application console are the following messages2:
main: got my port: <port:(127.0.0.1,3324),1>
Storing entry with key <Node: (127.0.0.1,3324) >
Essentially the application is storing in its protocol table that it has started a negotiation with the node at (127.0.0.1,3324) (i.e., m). Furthermore, the application is keeping track of the PLAN port it uses to communicate with its local router; this port contains the session id for this application (1).

In the node's log file, we find the following messages are printed after the receipt of the first message:
Authproto: [ DHmessageOne ] with cert (<blob:566>,<blob:38>,93051
9501,930520101,0,(127.0.0.1,3324),((127.0.0.1,3324),<port:(127.0.
0.1,3324),1>))
Storing entry with key <Application: ((127.0.0.1,3324), <port:(12
7.0.0.1,3324),1>)>
This indicates that the authentication request was received and the protocol was initiated for the application on node (127.0.0.1,3324), which has session id 1. The node sends the response, which causes the app to print
Authproto: [ DHmessageTwo ] with cert (<blob:566>,<blob:39>,930519
505,930520105,0,0,<blob:309>,<blob:566>,((127.0.0.1,3324),<port:(1
27.0.0.1,3324),1>),(127.0.0.1,3324))
Finding entry with key <Node: (127.0.0.1,3324) >
Calculated shared secret |2125718927524411328493843923218892174119
922226269965517999906850471020703992323480943083011891723260923569
786035298658358526807900077466329250605511687325263329121557366026
123831338589432298164307582211644566693860112609214362571249075333
519598834822874461197794695987211289164577495723370914058267031267
2334|
sSPI=0
rSPI=0
Updating entry with key <Node: (127.0.0.1,3324) >
Authapp indicates it has received the message from the node that contains the node's public value. From this and its own secret (now generated), authapp creates the shared secret which it stores in a table. The shared secret is printed to the console, followed by the generated sender and receiver SPI's for the exchange (sSPI and rSPI, respectively). Finally, the node receives the final message from the application, logging:
Authproto: [ DHmessageThree ] with cert (<blob:566>,<blob:39>,9305
19512,930520112,0,0,<blob:308>,<blob:566>,(127.0.0.1,3324),((127.0
.0.1,3324),<port:(127.0.0.1,3324),1>))
Finding entry with key <Application: ((127.0.0.1,3324), <port:(127
.0.0.1,3324),1>)>
Calculated shared secret |2125718927524411328493843923218892174119
922226269965517999906850471020703992323480943083011891723260923569
786035298658358526807900077466329250605511687325263329121557366026
123831338589432298164307582211644566693860112609214362571249075333
519598834822874461197794695987211289164577495723370914058267031267
2334|
sSPI=0
rSPI=0
Updating entry with key <Application: ((127.0.0.1,3324), <port:(12
7.0.0.1,3324),1>)>
Here we see that the shared secret and the SPI's calculated are the same. When you run this code, the shared secret will not have the same value as the one shown here, but the SPI's should be the same (since they all start counting from 0). The node stores this information in its tables.

Finally, the application executes its user function. This function creates a chunk which simply prints a message ``authenticated successfully.'' It signs this chunk and sends it the remote node, providing the chunk and the signature as arguments to authEval. If authentication succeeds, the message will be printed at the application, otherwise an exception will be raised. The application prints:
Finding entry with key <Node: (127.0.0.1,3324) >
Signing tuple |(<blob:43>,0,1)|
Data = |[8][0][0][0][3][6][0][0][0]+[13][0][4]doit[0][0][0][1][2][
0][4]doit[0][0][0][2][0][5]print[0][1][2][0][7]authSvc[0][0][1][0]
[0][0][0][1][0][0][0][1]|
 Sign = |[23]x@G[237]_[246]_[251][220]x[229]|
Sending test message
In the node log, we see the message
Verifying tuple |(<blob:43>,0,1)|
Data = |[8][0][0][0][3][6][0][0][0]+[13][0][4]doit[0][0][0][1][2][
0][4]doit[0][0][0][2][0][5]print[0][1][2][0][7]authSvc[0][0][1][0]
[0][0][0][1][0][0][0][1]|
 Calc |[23]x@G[237]_[246]_[251][220]x[229]| =?
 Sign |[23]x@G[237]_[246]_[251][220]x[229]|
Here we see that the node verifies that the provided signature matches the calculated one. Then, as a result, the application prints the message
127.0.0.1:3324 authenticated successfully
Authapp as a template
The authapp program was written as a template as well as a demonstration program. It has been structured to allow easy modification for your own purposes.

Assuming that the first thing you want your application to do is to authenticate with a node, you need only modify the function user_main to perform the appropriate actions. This function is of type Authproto.principal_addr × Activehost.activehost × (Basis.active_packet ® unit) × (unit ® string). The first argument is the peer just authenticated with (either a node or an application), the second is address of the local PLAN interpreter, the third is the function used to send packets to the interpreter, and the fourth may be used to receive data from the interpreter (presented as strings). When the user_main function returns, the connection to the local interpreter is closed and the program exits.

It is slightly more complicated if you want to authenticate with multiple nodes and perhaps perform different actions with each of them. In this case, you have to pull the authentication code out of main into a separate function which may be repeated, perhaps on a per-thread basis.

2.3.2   Node-Node Authentication

The PLAN daemons are also equipped with the ability to authenticate with other nodes; this ability is particularly important to the firewall application that we will illustrate in Section 3.3.2.

When a node starts, it may be given a list of hostnames to its -authlist parameter to indicate the other nodes to authenticate with. For each address given in the list, it waits until an entry may be found in the RIP routing table for that node, and then initiates a connection to it. This allows a node-node connection to be possible for any node on the network, as opposed to just adjacent nodes (which could be achieved by looking in the ARP table for the requested recipient). However, this technique leverages the fact that the RIP routing implementation does not having ``default'' routing table entries. As an alternative, we could have simply attempted to send the initiation message, retrying if the destination was determined to be unreachable along the way.

We present the tutorial by starting two nodes, as in the PLAN tutorial [3], one on port 3324 and the other on port 3325. We start the one on port 3325 first, just as in the tutorial:


% bin/pland -l log3325 -ip 3325 m2


Then we start the one on 3324, nearly the same as in the tutorial, but we also specify m:3325 as an argument to -authlist:


% bin/pland -l log3324 -ip 3324 -authlistm:3325 m1




The initiating node (m:3324) first waits for the receiving node to become accessible:
trying to authenticate with node (127.0.0.1,3325)
currently unknown
trying to authenticate with node (127.0.0.1,3325)
Once the receiver is known, the initiation message is sent. The receiving node processes the initiation message, calculates its portion of the shared secret and sends the response to the initiating node:
Authproto: [ DHmessageOne ] with cert (<blob:566>,<blob:38>,930513
828,930514428,0,(127.0.0.1,3325),(127.0.0.1,3324))
Storing entry with key <Node: (127.0.0.1,3324) >
The initiating node receives the response, calculates its own portion of the shared secret, and using the information in the message received, calculates and stores the shared secret:
Storing entry with key <Node: (127.0.0.1,3325) >
Authproto: [ DHmessageTwo ] with cert (<blob:566>,<blob:39>,930513
832,930514432,0,0,<blob:308>,<blob:566>,(127.0.0.1,3324),(127.0.0.
1,3325))
Finding entry with key <Node: (127.0.0.1,3325) >
Calculated shared secret |2576912389757616131645963461487159443026
571643347028392844167107049555585165992701860118710028039629843832
243076311528753543566417243156174533504726150218095603906742182340
692675347036154605455462926931605367360169111711582680304704305625
472475762774815248463312144731495286620602337979104139886906037797
263|
sSPI=0
rSPI=0
Updating entry with key <Node: (127.0.0.1,3325) >
Finally, the receiving node receives the other piece of the shared secret, and calculates and stores the full secret.
Authproto: [ DHmessageThree ] with cert (<blob:566>,<blob:39>,9305
13839,930514439,0,0,<blob:308>,<blob:566>,(127.0.0.1,3325),(127.0.
0.1,3324))
Finding entry with key <Node: (127.0.0.1,3324) >
Calculated shared secret |2576912389757616131645963461487159443026
571643347028392844167107049555585165992701860118710028039629843832
243076311528753543566417243156174533504726150218095603906742182340
692675347036154605455462926931605367360169111711582680304704305625
472475762774815248463312144731495286620602337979104139886906037797
263|
sSPI=0
rSPI=0
Updating entry with key <Node: (127.0.0.1,3324) >
Multiple protocol exchanges may occur simulataneously and will be stored properly in the resulting tables.

2.4   Implementation Notes

The code used to implement the authentication protocol may be found in the security directory in the PLAN distribution. The file authproto.ml contains the guts of the code that does the protocol operations. The protocol is initiated by calling initiate_dh_exchange. Each of the exchanged messages is processed by dh_message_one, dh_message_two, and dh_message_three, respectively, and the certificate to be sent in response is returned. Each one of the protocol-processing functions is called by wrappers appropriate to the principal type -- either node or application (recall that for the former, the protocol messages take the form of PLAN programs making service invocations, while the the latter are delivered tuples) -- and sends the response. The PLAN services used in the protocol are defined in authproto_svc_impl.ml. The code for the node-node authentication may be found in nodeauth.ml.

The file auth.ml implements functions for authentication, both DSA-based for the protocol, and HMAC-SHA1 for subsequent authentications. It also provides services used by authEval for mapping a principal to a thread id, and a service for retrieving this mapping, used by the authorization functions. Autheval itself is defined with a cast of supporting functions in crypto_svc_impl.ml. The DSA implementation resides in the crypto directory.

The messages printed during the authentication are printed because the compilation flag -DAUTH_DEBUG is on by default. To eliminate these messages, recompile without this flag (remove it from the CAMLP4FLAGS variable in the Makefile).

Finally, the protocol is computationally intensive, and it is often the case that the initiator completes his portion of the calculation (and is thus ready to use the secret) before the receiver. To compensate, we put a timer in authapp to wait for the receiver to finish. You may find that you have to bump up this timer on slower machines. A more robust solution would be to have the receiver send a completion message.


Previous Next Contents