Secureit-Echat

To demonstrate the power of E for building distributed secure software, we have built a simple chat system. Securit-Echat allows two people to set up a secure conversation that cannot be penetrated or eavesdropped by any known technology. This entire chat system, written in E, takes only 5 pages if printed on paper, and as such is short enough to be discussed in detail.

The procedure for using Secureit-Echat is as follows:

The only step in this process that is vulnerable to outsiders is the step wherein you email the reference file to your friend. I.e., if a malicious third party intercepted the email, such a person could pick up the reference and present himself as your friend. This security hole can be easily closed by using a secure email system, for example by using the very easy to install PGP upgrade to Outlook Express or Eudora.

Logging in to the Internet before creating your reference file is important only if you are using a conventional ISP connection to the Web. In this case, your IP address is assigned by the ISP when you log in; if you create your reference file before logging in, the reference file will not contain the ISP-supplied IP address.. If you have a preconfigured IP address, the sequencing is not important.

The key architectural concept for maintaining security is to be careful about who gets a reference to an object in the first place, because if the trespasser cannot get a reference, he cannot breach your security. This architectural philosophy is employed in the way Secureit-Echat sets up a connection, wherein the only way for someone to find you is to get their hands on the reference document. By ensuring that only your friend can get the reference document, you ensure security (as long as the friend can be trusted; but if he cannot be trusted, no technique could give you security anyway).

The general strategy of only enabling a capability or eright when someone needs it and can be trusted with it is also found in the user interface of the Secureit-Echat program. Note that, until you have filled in your name, all other buttons in the program are disabled. Similarly, the Send button is not enabled until a connection is in place. Although E can in principle allow the sending of messages to a friend who does not yet have a connection (by sending the message through the promise of a friend), in practice in the 2-person Echat program it makes little sense for the user to do so, so the ability is not enabled.

This technique of denying the user a capability until he can use it effectively is well understood by user interface designers and is quite commonly employed. One way of describing E's security architecture is to say that E rigorously employs this technique throughout every layer of software, down to the level where software meets hardware and acquires the ability, for example, to reset the system clock.

With that introduction, here is the source for Secureit-Echat:

After setting up the traceln debugging routine, we set up abbreviations that allow us to easily access the parts of the Java API most frequently used in the program, the Swing and AWT packages:


# set up tracing; stub out all the printing for operational version

define traceln(str) {

#    println(str)

#    str

}



# set up imports

define swingPkg := import:com.sun.java.swing.*

define awtPkg := import:java.awt.*

define swing(className) {

    swingPkg[className] 

}

define awt(className) {

    awtPkg[className] 

}

Next, we put the introducer on the air so our connection operations can come to life, and create a couple of utility routines that convert URIs to and from sturdy references. Note the important security check done at this stage: the program checks to see whether it is running on secure incrypted E or on insecure DaffE, and warns the user if the chat session is set up in the clear.


# Ensure the user knows if he's using a clear, unencrypted connection

traceln(introducer negotiable[0])

if (introducer negotiable[0] != "3DES_SDH_M") {

    swing("JOptionPane") showMessageDialog(null, 

    "You are using DaffE, so you'll be chatting in the clear.

    To be secure, use E instead.", 

    "Unencrypted DaffE Session", 

    swing("JOptionPane") WARNING_MESSAGE)

}



introducer onTheAir

traceln(introducer)



Utility routines for reading and writing the reference document, opening the dialogs and putting them in usable form




# return the object represented by the URI

define getObjectFromURI(uri) {

    introducer sturdyFromURI(uri) liveRef

}



define makeURIFromObject(obj) {

    introducer sturdyToURI(sturdyRef(obj))

}



# return the friend file

define findFriendFile(chatWin) {

    define dialog := awt("FileDialog") new(chatWin, "Select a Friend")

    dialog show

    define path := dialog.file

    if (path != null) {

	path := dialog.directory + path

    }

    file: path

}



# return a file to be saved

define requestSaveFile(chatWin) {

    define dialog := awt("FileDialog") new(chatWin,

                                           "Save File with Your Name",

                                           awt("FileDialog") SAVE)

    dialog show

    define addressName := dialog.file

    if (addressName != null) {

	addressName := dialog.directory + addressName

    }

    file: addressName

}



# method that writes out the URI for your echat system's communication

# interface 

define offerMyAddress(file, uri) {

    file.text := uri

} 



The set1LineComponentParameters method is a utility for setting up buttons and textfields so they'll resize in an intelligent fashion when the user resizes the chat window.


define set1LineComponentParameters(component) {

    component.preferredSize := awt("Dimension") new (150,25)

    component.maximumSize := awt("Dimension") new(1000,25)

    component.alignmentX := 0.5

} 

The addComponent routine is a utility for dealing with a subtlety in E's interaction with Java objects: the Java Container class has many methods with the signature "add(object)", for which Java discriminates among the choices by looking at the type of the object. Since E is not strongly typed, it cannot make the same discrimination. Hence when using the add method for Containers, you have to revert to the primitive E method E call(...). Here we encapsulate the usage of this low level E method for adding components to containers.


define addComponent(container, component) {

    traceln("addcomp1 ui made");   

    E call(container, "add(Component)", [component]);

    traceln("addcomp2 ui made");

}

The first of the two big classes in Echat is the chatUI class, which constructs the chat window, its buttons, and the listener objects. Though there is a lot of code here, little of it is new; it is mostly just calls from E to the elements of Java's awt and swing classes needed to construct a window. The flow of control is essentially identical to the flow one would build in Java to achieve the same purpose. In some situations it could make sense to use a Java GUI builder to create the window code in Java itself, and then hook it up to E with import:. But since we wanted the chat window to be intelligently resizable, it was beyond the abilities of current Java GUI builders to provide it, and it turned out to be easier to construct such resizable windows in a directly-executable scripting language like E than in Java.

One element of chatUIMaker that is worth examining is the definition of myWindowListener. In Java, WindowListener is an interface that defines several methods to respond to different events. We are only interested in one event, the windowClosing event, so our windowListener simply uses a match _ {} statement to intercept and ignore all the messages other than windowClosing. A Java programmer would achieve a similar, though still larger and more complex effect, by creating a subclass of the WindowAdapter class and overriding the one method that interested him.

The other interesting feature of myWindowListener is that, after telling the chatController to leave (i.e., to terminate the conversation), it tells the interpreter to continue at top. This statement undoes the blockAtTop initiated as the last line of the program that keeps the interpreter from rushing off to the end of the program and terminating everything before things have hardly started.

The one important feature of the chatUIMaker for the other components of the program is the chatUI object constructed at the end of the chatUIMaker. This object gives outside objects (notably the chatController object) access to the widgets which it must manipulate (for example by enabling and disabling the buttons, and putting new messages into the chatTextArea). The chatUIMaker must receive a chatController so that the Listeners can send appropriate messages to the chatController when the user interacts with the window; this requirement for the chatUI to have a reference to the chatController imposes interesting design considerations on the chatControllerMaker, as described in the preface to the chatControllerMaker following the chatUIMaker source:


define chatUIMaker new(chatController) {

    # Lay out the chat window, create its components

    define chatWin := swing("JFrame") new("Secure EChat")

    define chatPane := chatWin.contentPane

    define border := swing("BoxLayout") new(chatPane,1)

    chatPane.layout := border

    traceln("p1 ui made");

    define windowListener {

	to windowClosing(event) {

	    chatController leave

	    traceln("trying to exit")

	    interp continueAtTop

	}

	match _ {}

    }

    chatWin addWindowListener(windowListener)

    traceln("p2 ui made");

    

    # adds a button to the chatPane that calls the chatController

    define addButton(labelText, verb) {

	define button := swing("JButton") new(labelText)

	set1LineComponentParameters(button)

	addComponent(chatPane, button)

	define buttonListener {

	    to actionPerformed(event) {

		E call(chatController, verb, [])

	    }

	}

	button addActionListener(buttonListener)

	button

    }



    # setNameButton

    define setNameButton := addButton("Set Your Name", "setMyName")

    

    # offerChatButton

    define offerChatButton := addButton("Offer Chat", "offerSelf")

    offerChatButton.enabled := false

    

    # findFriendButton

    define findFriendButton := addButton("Find Friend", "findFriend")

    findFriendButton.enabled := false



    # chatScroller that holds chatTextArea

    define chatScroller := swing("JScrollPane") new

    chatScroller.maximumSize := awt("Dimension") new(2000,1000)

    chatScroller.preferredSize := awt("Dimension") new(250,80)

    addComponent(chatPane, chatScroller)

    

    # chatTextArea

    define chatTextArea := swing("JTextArea") new

    chatTextArea.lineWrap := true

    addComponent(chatScroller.viewport, chatTextArea)

    

    # nextMessageBox

    define nextMessageBox := swing("JTextField") new(

        "Type your message here",30)

    addComponent(chatPane, nextMessageBox)

    traceln("p3 ui buildt");



    # sendMessageButton

    define sendMessageButton := addButton("Send", "send")

    sendMessageButton.enabled := false

    

    chatWin pack

    chatWin show

    

    define chatUI {

	to getChatWin { chatWin }

	to getNameButton { setNameButton }

	to getOfferChatButton { offerChatButton }

	to getFindFriendButton { findFriendButton }

	to getChatTextArea { chatTextArea }

	to getNextMessageBox { nextMessageBox }

	to getSendMessageButton { sendMessageButton }

    }

}

The chatController contains all the interesting concurrency behavior. It is also the only object in Secureit-Echat that is exposed to another person's software. As such, it is the only object that gives other people any erights inside your computer, and thus the only object that must be inspected in this system for security issues. We discuss the security issues of the Secureit-Echat program later. For now, let's look at the interesting architectural features. First, note that there are 2 definitions of chatController, one enclosed at the end of the definition of the other. Why?

As you recall from the chatUI discussion, to compose a chatUI object, you first need a chatController. However, to compose a chatController, you first need a chatUI object (note that the first thing done in the chatController is the definition of a chatUI). This is a classic case of 2 objects needing references to each other essentially as soon as they come to life, a problem that has plagued programmers ever since the invention of the object. Here we use the E technique of immediately using a variable's name inside the scope of the code block that defines the object: the chatController being defined is passed to the chatUIMaker as the first step in the chatController's definition. And since the second definition of chatController is the last step in the definition of the first chatController, at the end of the definition, the first chatController effectively becomes the second chatController, and everything works out cleanly.

Interesting methods in the chatController include

The last part of the program contains the lines that start up the entire world of Echat objects, and then block the interpreter from terminating before the user is done with the program:

? define controller := chatControllerMaker new



? interp blockAtTop

And that is the whole program.

Security considerations

As noted earlier, the total security analysis of Secureit-Echat boils down to an assessment of the appropriateness of the erights granted by the 10 public methods of the chatController. If these 10 methods grant only erights that are appropriate for the person at the other end of the conversation, even if your friend turns out to be less than trustworthy, little harm can come to you or your computer. But if, for example, there were methods in chatController to ReadADirectory and ReWriteAFile, your computer would be exposed to considerable danger, because a malicious chatter could destroy your hard disk, steal your company proprietary data, and install a virus that would watch for your password the next time you turned on your Quicken checkbook.

How secure is this version of Echat? The answer is, probably secure enough for this application, but still not as secure as a purist might prefer. This chatController mixes 2 different sets of methods in its public interface: methods that must be public so the listeners in the chat window can call them, and methods that must be public so that the friend's chat program can call them.  The methods that are public for the chat window's listeners could provide security holes for the remote program to exploit. Let's look at a couple of things pernicious "friends" might do by modifying their version of the Secureit-Echatprogram.

First, they might issue a setMyName command to your computer, which would be irritating because it would pop up a dialog box on your computer, though beyond that the damage is negligible. Similarly a "friend" could tell your program to offerSelf, again popping a dialog box. They could also make you send() repeatedly, but this would only make you send whatever you had in your nextMessage text field, so again the harm is quite limited. Really, the worst harm such a  "friend" could do is persuade you that the Secureit-Echat program itself was unreliable (because it would pop dialog boxes and send incomplete messages for no visible reason).

How difficult is it to fully secure this program, so that even a friend who was in cahoots with Satan (or the Internal Revenue Service... is there a difference?) could not do anything except chat? The answer is, it takes a handful of additional lines of code, and the modification of one existing line of code.

The solution is to create a chatReceiver object that acts as a facet for the chatController, passing through only those messages that the friend is allowed to send. The chatReceiver looks like this:




    # facet of chatController sent to other chatter with only appropriate messages

    define chatReceiverMaker new(chatController) {

        define chatReceiver {

    	to receive(message) { chatController receive(message) }

    	to receiveFriend(friend, name) {

    	    chatController receiveFriend(friend, name)

    	}

    	to friendIsLeaving { chatController friendIsLeaving }

    	to revoke { chatController := null }

       }

    }

    ...

    # (now inside the chatController definition)

    define myChatReceiver := chatReceiverMaker new(chatController)

Note that the chatReceiver defines one method in addition to the methods it forward to the chatController: the "revoke" method. Once the chatReceiver is made the complete interface to the outside world (shown later), simply nulling out its connection to the rest of the program shuts down all communication and all risk of outside tampering. This revocation is appropriately performed inside the disconnect method:

    #(inside the disconnect method)

    myChatReceiver revoke

The parts of the chatController public interface that are strictly for local usage simply don't appear in the chatReceiver, so the friend's program has no access to them--as long as we send the friend a reference to the chatReceiver rather than the chatController. We switch to a chatReceiver-based communication system, by replacing the reference to the chatController with a reference to the chatReceiver, in the following 2 lines of code from the chatController:

   # (inside of offerSelf)

   offerMyAddress(myAddressFile,

       makeURIFromObject(myChatReceiver))
...

   #(inside of findFriend)

   define name := friend <- receiveFriend(myChatReceiver,

          myName)



That's it! Secureit-Echat is now secure even from your friends who are secret agents of the CIA! :-)