ERights Home elang 
Back to: E Tools On to: Help Using E

Secure EChat


To demonstrate the power of E for building distributed secure software, we have built a simple chat system that 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 Secure EChat is as follows:

  • Turn on your Secure EChat program.
  • Set the name by which you wish to be referred during this chat session (NOTE: Fix program so this is not required!)
  • Log in to the Internet.
  • Offer yourself for a chat session by writing out a reference file. The reference file contains the URI of your EChat controller object, which is described in detail below, though as a user, you only need to give the program a file name and it will do the rest.
  • Email your reference file to the friend with whom you wish to chat
  • Your friend mimics most of these steps, turning on a copy of the Secure EChat program, sets their name, logging into the Internet. But instead of offering themselves for chat by writing a reference file, they find you by reading your reference file.
  • The software sets up a secure connection and each of you can start sending the other messages.

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 Netscape Communicator ***?, 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, wherein your IP address is assigned by the ISP when you log in. If you have a preconfigured IP address, the sequencing is not important.

As explained in the discussion of capability-based security, 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 Secure 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 Secure EChat program. Note that, until you have filled 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.

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 Secure EChat:

This code is rather out of date, so to prevent it from being updoc'ed, we start by exiting with success:

? pragma.syntax("0.8")

? interp.exitAtTop()

First 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 imports
? def swing := <import:com.sun.java.swing.*>
? def awt := <import:java.awt.*>

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:

? introducer.onTheAir()


# return the object represented by the URI
? def getObjectFromURI(uri) :any {
>     introducer.sturdyFromURI(uri).getRcvr()
> }

? def makeURIFromObject(obj) :any {
>     introducer.sturdyToURI(sturdyRef.temp(obj))
> }

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

# return path to the friend file
? def findFriendFileName(chatWin) :any {
>     def theDialog := awt("FileDialog")(chatWin,"Select a Friend")
>     theDialog.show()
>     def path := theDialog.getFile()
>     if (path != null) {
>         path := theDialog.getDirectory() + path
>     }
>     return path
> }

replace with drag/drop the file?
# return a pathName for a file to be saved
? def requestSaveFileName(chatWin) :any {
>     def theDialog := awt("FileDialog")(chatWin,
>                                        "Save File with Your Name",
>                                        awt("FileDialog").getSAVE())
>     theDialog.show()
>     def addressName := theDialog.getFile()
>     if (addressName != null) {
>         addressName := theDialog.getDirectory() + addressName
>     }
>     return addressName
> }

? def offerMyAddress(fileName, theURI) {
>     <file>[fileName].setText(theURI)
> } 

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

? def set1LineComponentParameters(theComponent) {
>     theComponent setPreferredSize(awt("Dimension") new (150,25))
>     theComponent setMaximumSize(awt("Dimension") new(1000,25))
>     theComponent setAlignmentX(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. XXX no longer true now that we have verb-string syntax

? def addComponent(container, component) {
>     E.call(container, "add(Component)", [component])
> }

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 strategy is identical to that introduced at the end of the (???) chapter.

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

? def chatUIMaker(chatController) :any {
>     # chatWin
>     # Lay out the chat window, create its components
>     def chatWin := swing("JFrame")("Secure EChat")
>     def chatPane := chatWin.getContentPane()
>     def myBorder := swing("BoxLayout")(chatPane,1)
>     chatPane.setLayout(myBorder)
>     def myWindowListener {
>         to windowClosing(theEvent) {
>             chatController.leave()
>             interp.continueAtTop()
>         }
>         match _ {}
>     }
>     chatWin.addWindowListener(myWindowListener)
>
>     # setNameButton
>     def setNameButton := swing("JButton")("Set Your Name")
>     set1LineComponentParameters(setNameButton)
>     addComponent(chatPane, setNameButton)
>     def myNameButtonListener {
>         to actionPerformed(theEvent) {
>             chatController.setMyName()
>         }
>     }
>     setNameButton.addActionListener(myNameButtonListener)
>
>     # offerChatButton
>     def offerChatButton := swing("JButton")("Offer Chat")
>     offerChatButton.setEnabled(false)
>     set1LineComponentParameters(offerChatButton)
>     addComponent(chatPane, offerChatButton)
>     def myOfferButtonListener {
>         to actionPerformed(theEvent) {
>             chatController.offerSelf()
>         }
>     }
>     offerChatButton.addActionListener(myOfferButtonListener)
>
>     # findFriendButton
>     def findFriendButton := swing("JButton")("Find Friend")
>     findFriendButton.setEnabled(false)
>     set1LineComponentParameters(findFriendButton)
>     addComponent(chatPane, findFriendButton)
>     def myFriendButtonListener {
>         to actionPerformed(theEvent) {
>             chatController.findFriend()
>         }
>     }
>     findFriendButton.addActionListener(myFriendButtonListener)
>
>     # chatScroller that holds chatTextArea
>     def chatScroller := swing("JScrollPane")()
>     chatScroller setMaximumSize(awt("Dimension")(2000,1000))
>     chatScroller setPreferredSize(awt("Dimension")(200,60))
>     addComponent(chatPane, chatScroller)
>
>     # chatTextArea
>     def chatTextArea := swing("JTextArea")()
>     chatTextArea.setLineWrap(true)
>     addComponent(chatScroller.getViewport(), chatTextArea)
>
>     # nextMessageBox
>     def nextMessageBox := swing("JTextField")("Type your message here",30)
>     addComponent(chatPane, nextMessageBox)
>
>     # sendMessageButton
>     def sendMessageButton := swing("JButton")("Send")
>     sendMessageButton.setEnabled(false)
>     set1LineComponentParameters(sendMessageButton)
>     addComponent(chatPane, sendMessageButton)
>     def mySendButtonListener {
>         to actionPerformed (theEvent) {
>             chatController.send()
>         }
>     }
>     sendMessageButton.addActionListener(mySendButtonListener)
>
>     chatWin.pack()
>     chatWin.show()
>
>     def 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 Secure EChat that is exposed to another person's software, and as such, 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 Secure EChat program later. For now, let's look at the interesting architectural features. First, note that there are 2 definitions of chatController (note, get rid of inner and outer 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 presented in chapter ??? 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

  • offerSelf, which creates the file containing the chatController's URI.
  • findFriend, which creates a live reference to a friend based on a URI found in a file, and sets up the connection,
  • send and receive, which simply pass messages across and are indeed as simple as their job suggests--receive is just one line long, while send is long enough to both send the message to the friend and to post the message locally on the sender's chat window. All the effort to encrypt and decrypt these messages are performed invisibly by E for the programmer, it is simply and marvelously invisible. Here is the code:
    ? def chatControllerMaker() {
    >     def outerChatController := {
    >         def chatUI := chatUIMaker(outerChatController)
    >         def myName := null
    >         def myFriend := null
    >         def myFriendName := null
    >         def myAddressFileName := null
    >         def showMessage(senderName, theMessage) {
    >             chatUI.getChatTextArea().append(senderName)
    >             chatUI.getChatTextArea().append(" says:\t")
    >             chatUI.getChatTextArea().append(theMessage + "\n")
    >         }
    >         def innerChatController {
    >             # transmitting functions
    >             to send() {
    >                 def nextMessage := chatUI getNextMessageBox getText
    >                 println("next message" + nextMessage)
    >                 myFriend <- receive(nextMessage)
    >                 showMessage(myName,nextMessage)
    >             }
    >             to setMyName() {
    >                 myName := swing("JOptionPane").showInputDialog(
    >                         "Please give me your name for this chat session");
    >                 # myName := "marcs"
    >                 if (myName != null) {
    >                     chatUI.getNameButton().setLabel(myName)
    >                     chatUI.getNameButton().setEnabled(false)
    >                     chatUI.getFindFriendButton().setEnabled(true)
    >                     chatUI.getOfferChatButton().setEnabled(true)
    >                 }
    >             }
    >             to offerSelf() {
    >                 myAddressFileName := requestSaveFileName(chatUI getChatWin)
    >                 if (myAddressFileName != null) {
    >                     offerMyAddress(myAddressFileName,
    >                                    makeURIFromObject(outerChatController))
    >                 }
    >             }
    >             to leave() {
    >                 if (myAddressFileName != null) {
    >                     <file>[myAddressFileName].delete()
    >                 }
    >                 myFriend <- friendIsLeaving()
    >                 innerChatController.friendIsLeaving()
    >             }
    >             to receive(theMessage) {
    >                 showMessage(myFriendName,theMessage)
    >                 1
    >             }
    >             to receiveFriend(theFriend) {
    >                 myFriend := theFriend
    >                 chatUI.getSendMessageButton().setEnabled(true)
    >                 1
    >             }
    >             to receiveFriendName(theName) {
    >                 myFriendName := theName
    >                 # chatUI.getChatTextArea().append(myFriendName + " has arrived\n")
    >                 1
    >             }
    >             to sendName {
    >                 myName
    >             }
    >             to findFriend {
    >                 def fileName := findFriendFileName(chatUI getChatWin)
    >                 if (fileName != null) {
    >                     def friendURI := <file>[fileName].getText()
    >                     showMessage("me", "uri" + friendURI)
    >                     myFriend := getObjectFromURI(friendURI)
    >                     showMessage("me", "obj" + myFriend)
    >                     def result := myFriend <- receiveFriend(innerChatController)
    >                     Ref.whenResolved(result, def observer(r) {
    >                         if (Ref.isBroken(r)) {
    >                             showMessage("me", "got clobbered")
    >                         } else {
    >                             showMessage("me", "won against all odds")
    >                         }
    >                     })
    >                     showMessage("me","sent me")
    >                     myFriend <- receiveFriendName(myName)
    >                     myFriendName := myFriend <- sendName()
    >                     chatUI.getSendMessageButton().setEnabled(true)
    >                 }
    >             }
    >             to friendIsLeaving() {
    >                 myFriend := null
    >                 myFriendName := null
    >             }
    >         }
    >     }
    > }
    

The last part of the program is 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:

? def controller := chatControllerMaker()

? interp.blockAtTop()

And that is the whole program.

Security considerations

As noted earlier, the total security analysis of Secure 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 WriteAFile, 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. (unless you were running Secure EChat on top of the SafeEMachine, described elsewhere, in which case a malicious access to these abilities would throw an alert out to the user).

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. Let's look at a couple of things pernicious friends might do with a modified version of the Secure EChat program: 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 friends 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 Secure 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 sent by Satan (or the Internal Revenue Service... is there a difference?) could not do anything except chat? The answer is, it takes ??? 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:

def chatReceiverMaker new(chatController) {
    def chatReceiver {
        to receive(theMessage) {chatController.receive(theMessage)}
        to receiveFriend(theFriend) { chatController.receiveFriend(theFriend)}
        to receiveFriendName(theName) {chatController.receiveFriendName(theName) }
        to sendName {chatController.sendName() }
	to friendIsLeaving{chatController.friendIsLeaving()}
        match _ { }
    }
}
...
# (now inside the chatController definition)
def myChatReceiver := chatReceiverMaker(chatController)

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, by replacing the reference to the chatController with a reference to the chatReceiver, in the following line of code from the chatController's offerSelf method:

# (inside of offerSelf)
offerMyAddress(myAddressFileName, makeURIFromObject(myChatReceiver))


That's it! Secure EChat is now secure even from your friends who are in cahoots with the CIA! :-)

 

 
Unless stated otherwise, all text on this page which is either unattributed or by Mark S. Miller is hereby placed in the public domain.
ERights Home elang 
Back to: E Tools On to: Help Using E
Download    FAQ    API    Mail Archive    Donate

report bug (including invalid html)

Golden Key Campaign Blue Ribbon Campaign