Restricted-channel multicast in Java

How a desktop stock-ticker app was converted into a secure system, using an applet running on a smart card device

This article describes the modifications made to an existing multicast application during research and development at Sun Microsystems. The goal was to convert an application bundled with Java Reliable Multicast Service (JRMS) to something we are calling a restricted-channel system. In a restricted-channel system, the multicast server transmits encrypted information that can be deciphered only by authorized multicast client programs or multicast client programs operating under authorized conditions. For security reasons, a Java Card was chosen as the device on which to perform decryption. The program performing the decryption was run by the smart card chip itself. The system envisioned in this project is a complete one, where both decryption and payment are handled by the Java Card device. This article also demonstrates the use of the Java Card as a "purse," performing debit and credit card functions and actually "holding" money.

Over the past month we have been working on a project to create a prototype for a restricted-channel multicast system using rudimentary Java Card encryption. Multicast describes a system in which one message is transmitted to many listeners at the same time. On the Internet, or any network with a bus architecture, every machine on the network receives all packets -- no matter who the packets are meant for. This situation is invisible to users because a network node ignores any packets not specifically addressed to it. However, there is a special "multicast address" reserved on the Internet for communication intended for many recipients.

In this restricted-channel system, the multicast server transmits encrypted information, which can only be deciphered by either: (a) authorized multicast client programs, or (b) multicast client programs operating under authorized conditions, such as those submitting payment for the service.

Our goal has been to investigate the feasibility of creating such a system using the Java Reliable Multicast Service (JRMS). Reliable multicast is distinguished from standard multicast in that any and all packets transmitted over the network must be received correctly, or the client will ask the server to continually retransmit until a correct packet is received. (TCP, the reliable unicast protocol used by the Internet, and UDP, its unreliable cousin, may be more familiar to some readers). For our project, we chose to take an existing demo system, designed merely to illustrate a possible use for JRMS, and change it into a restricted-channel system. The original client end of the system was a desktop stock-ticker window, implemented using standard Abstract Windowing Toolkit (AWT) classes and a JRMS multicast client socket. The original server provided the channel content (stock quotes) through a JRMS server socket. For the purposes of the demo only, the stock quotes were obtained either from a disk file or from a public Web site (Yahoo), which was parsed and retransmitted as multicast data.

Restricted-channel multicast

Why restricted-channel multicast? More than one person has asked us this. One possible application we've repeatedly invoked is the model of a reserved-for-top-executives information service within a corporation -- possibly a stock ticker or other, more sensitive information. However, other models exist.

For multicast services that require payment, you must have a restricted-channel system in place -- to prevent theft of service. Some sort of cryptosystem is the best way to implement a restricted channel. Even if payment is not an issue, a strong enough encryption scheme could allow even military or governmental multicast applications, where security is required for other reasons. Work orders could be multicast in realtime to different stations with a restricted-channel system. Software updates could be sent automatically over the Net to a large customer base. Upper-level management of large corporations could send updates on specials to restaurant franchise local managers, or warehouse managers.

The crucial aspect of our system is that our decryption applet runs directly on the Java Ring, not on the host system. If a ring or card stores only a key, the security of that key will be compromised during transmission to the decrypting process running on the host system. A serial port can be tapped or monitored, and within the operating system of a traditional user-interfaced, multi-interfaced, and/or distributed system -- such as a workstation or PC -- opportunities abound for theft of a key. In many respects, a Java Ring, and to a lesser extent a Java Card, presents an information cul-de-sac: There is only one point of information ingress or egress, and no user interface or distributed system. Smart cards offer even more functionality because they house a true computer -- with erasable and rewritable memory, a processor, and an operating system. Smart cards furnish a way for programs that use ultra-sensitive data to begin, execute, and finish without ever allowing any of the sensitive information to leave the cul-de-sac.

The majority of our modifications to the stock-ticker multicast system were on the client side. We elected to use a rudimentary (bitwise logical NOT) encryption scheme so that we could focus our efforts more strongly on the specifics of integrating on-ring decryption with an existing multicast system. Within the next four months, smart cards and rings implementing the Java Card spec will be available that perform 1024-bit key encryption.

While modifying the application, we found very little problematic code. Of a set of small problems, the largest was simply that of monitoring card and ring removal and reflecting this in program output. This problem was partially solved by the Open Card API itself.

One modification we made was to insert stubs (that is, program fragments with only limited functionality, meant to be filled in later by fully-developed code) for a smart card purse. This purse had a simple balance field and a debit method, which, when appropriate, threw an OutOfMoney exception. The stubs, unlike the decryption engine, did not run on the card. They will be replaced by a true Java Card purse in future versions.

The code

The sun.jrms.stock package originally contained three java files -- one debugging interface, and source for two executable classes: StockViewer and StockServer. My modifications were retitled SecureStockViewer and SecureStockServer, and were added to this package. Also available was the sunlabs.javacard.DecoderRing class for use by the SecureStockViewer. DecoderRing handled all direct access to the ring, using Dallas Semiconductor's iButton package imports, OpenCard imports, and so on. Additionally, I included a few inner classes and small outer classes, mostly to either override AWT methods or to provide stubs for the purse and for the DecoderRing class (so the front end of the program could be debugged when no ring was available).

The SecureStockViewer (the multicast client) has two main threads: one that downloads stock quotes and decrypts them (the run method), and another that scrolls the ticker display (the scrollmethod). In the initial algorithm, the run method continually downloaded new stock quotes from the multicast stream, converting each set of stock quotes to one long text string and placing it in a String object called message, which was merely a temporary holder for the data. The scrolling thread would display a String object called text. When a text string had finished scrolling completely, the scroll thread would call the following method:

private void
getNewData() {
    if (SecureStockViewer_Debug) {
    System.out.println("SecureStockViewer: getNewData.");
    }
    text = message;
    pos  = 0;
}

Assigning pos to zero restarts the scroller display at the beginning of text. Download was nearly instantaneous, while scrolling took a few minutes for each text string -- therefore, the program downloaded new stock quotes faster than it could display them. This would not have been a problem if not for the restricted multicast paradigm -- that is, the purse functionality (stubbed out, but still to be accounted for). Users should not have to pay for stock quotes they never see. To fix this problem, I introduced a flag (actually more like a latch) called waitingforscrolltofinish. When the decryption was finished, the download/decrypt method would hold until the scroller was finished as well, and then would charge the user for the download and start the cycle again by getting new stock quotes from the multicast connection.

Ring removal currently is detected in two ways. During decryption, the application detects removal by catching an exception -- any exception -- thrown by the decode method (the method in DecoderRing that sends a byte array, 60 bytes maximum, to the ring for decryption). This seemed at first to be a temporary solution, but further thought uncovered some logical justifications for actually leaving this feature in the design. When someone is paying for a service via a ring, he or she might naturally assume that ring removal should generate an event that halts the scroller and demands reinsertion of the ring. In this application, ring removal is only detected when a decryption attempt is made. Remember that the program was changed so that download and decryption now waits for the scroller to finish. This would leave a gap after decryption but before the scroller finished, meaning that the ring could be removed without stopping the scroller.

Is this a bug?

I say no, because the user is paying per download, not per second of usage. In fact, one could even argue that the user should pay per 60-byte segment decrypted. If the ring is removed during decryption, then the current segment should be dumped, and any previously decrypted segments should be displayed even if a quote is cut off in the middle! This is a very interesting issue, actually, leading to a consideration of the legal implications and discussions on questions such as, What does it mean to pay for something? Unfortunately, the issue is not within the purview of this article.

At present, the ticker halts whenever the ring is removed. After decryption, while the run method is waiting for the scroller to finish, it polls a method in DecoderRing called isDeviceInserted(). (See below for an explanation of how this relates to the Open Card API). To halt the scroller, we created a boolean flag called showErrLabel and a reference of type String called errLabel. If showErrLabel was raised, the scroll method would halt and set the error display to errLabel. The errLabel string has to reflect halt conditions, so it became the responsibility of the code that set the showErrLabel flag to also set this string to some descriptive value. This way, any code anywhere in the object could halt the ticker and display whatever message was necessary.

Another minor addition to the original StockViewer was to the user interface. A simple AWT label indicated how much "money" was left in the purse, which was decremented per download. I implemented a progress meter for the decryption process, indicating what percentage of the received message has been decrypted so far into plain text.

As a note, I was disappointed with the color when the program was run on different monitors. I tried to make the labels appear white on dark grey, with the stock ticker being yellow on black. On some monitors, however, the dark grey appeared more light grey.

Here's the code for the run method, where most of my changes were made. I've removed debugging print statements. Throughout the following explanation of the code for the run method, all commentary appears before the code it is commenting on.

The JRMS multicast packages returns data in UDP packets, hence the DatagramPacket object.

public void
    run() {
        boolean        received;
        DatagramPacket recvPacket  = null;

This is the outer loop, out of which there is no break statement. This thread stays in this method until a System.exit(#):

while (true) {

This flag goes up when a UDP packet is successfully received:

received = false;

If the flag does not go up, the program continues to try the ms.receive() method, as shown below. The ms field is a multicast socket object. This code was unchanged from that in StockViewer.

try {
    recvPacket = ms.receive();
    received   = true;
} catch (SessionDoneException sd) {
    System.exit(1);
} catch (SessionDownException sdwn) {
    System.exit(1);
} catch (IrrecoverableDataException ie) {
} catch (JRMSException se) {
    se.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

Below, allReceivedBytes holds the entire string that is downloaded. blockToDecode is used to hold the n-byte segment that is being sent to the ring for decryption, where n equals maxdecodelength. (In the case of the Dallas Semiconductor Java Ring using our particular applet, n equals 60, but other card or token device systems might allow more or fewer than this number of bytes to be sent to the device at a time.) offsets essentially indicates how many n-byte segments must be sent to the ring to fully decrypt the message.

if (received) {  // UDP packet received with encrypted stock data
    waitingforscrolltofinish = true;
    byte[] allReceivedBytes;
    byte[] blockToDecode = new byte[maxdecodelength];
    int offsets;
    allReceivedBytes = recvPacket.getData();
    offsets = allReceivedBytes.length / maxdecodelength;

The following for loop iterates once per n-byte (60-byte) block of code, plus once more for the tail-end of the data (which will most likely be of length less than n). The variable len keeps track of the length of the part of data that is being decrypted, so that len equals n (equals 60) in all cases except at the end of the data, when the tail-end is being decrypted, and len is equal to the remainder of the length of the data divided by n (allReceivedBytes.length/60).

The code below also resets the progress meter with each iteration. The number of blocks decrypted so far, divided by the total number of blocks, gives the percent downloaded. After having problems with AWT labels (they sometimes cut off part of the text) I extended Label with an inner class I called SafeLabel. SafeLabel keeps track of the minimum size for a label and changes it whenever the setText(String text) method is called, setting it to the width of text in pixels. It always returns this value to the LayoutManager with the getMinimumSize() method. This prevented, for example, the percent sign (%) getting cut off when the "Downloading:" label reached 100 percent.

int len;
for (int i = 0 ; i <= offsets ; i++) { // step through data n bytes at a time, where n = maxdecodelength
    String dloutput = "Downloading: " + NumberFormat.getPercentInstance().format((double)i/(double)offsets);
    lbDownloading.setText(dloutput);
    len =  (int)Math.min(maxdecodelength, allReceivedBytes.length - maxdecodelength * i);

The next code example attempts to decrypt the segment. If the ring is removed during decryption, the program will loop here until the ring is replaced. Note the check to javaring.isDeviceInserted() (javaring points to a DecoderRing object). This is redundant, as the caught exception produces the same effect within the if statement. It is not clear, however, whether or not this redundancy is unnecessary. The isDeviceInserted method depends on two Open Card methods called cardInserted and cardRemoved. There was some question as to whether these Open Card methods worked reliably with the iButton we were using. Since we weren't sure whether the call to isDeviceInserted was working reliably, we preserved the redundancy. When a successful decryption occurs, the error flag is lowered.

System.arraycopy(allReceivedBytes, i * maxdecodelength, blockToDecode, 0, len);

Copy the n-byte segment into blockToDecode.

while (true) { // try over and over to decode this 60-byte segment
    if (javaring.isDeviceInserted()) {
        try { // try to decode this segment
            blockToDecode = javaring.decipher(blockToDecode); // the decode call
            showerrlabel = false;
            break; // break out of while loop because decode of this segment was successful
        } catch (Exception e) {
            errlabel = ringisoutlabel;
            showerrlabel = true;
        } // try ; catch
    } else {
        showerrlabel = true;
        errlabel = ringisoutlabel;
    } // if(javaring.isDeviceInserted()) ; else
} // while(true)

Once a successful decryption occurs, the plain text is copied back into the original array in the exact same place where its cipher text was removed:

System.arraycopy(blockToDecode, 0, allReceivedBytes, i * maxdecodelength, len);

The program originally waited until the entire incoming multicast message was decrypted before displaying anything; then, while it downloaded a new set of quotes, it displayed the previous set. This would create a problem if the serial port or decryption introduced any kind of delay. Upon starting the program, the user would have to wait to see quotes that were already in memory and decrypted. To fix this problem, I modified the structure of the scroller method such that it actually displays all information as soon as it is available, even if the entire message has not yet been sent through the smart card device. The size of the "message" string grows with each new ring access, as new quotes are appended to those already decoded.

message = new String(allReceivedBytes).substring(0, i *
maxdecodelength + len);

When the ticker has finished decrypting, the download label "disappears":

} // for (int i = 0 ; i <= offsets ; i++)
if (firstdownload) firstdownload = false;
message = new String(allReceivedBytes);
lbDownloading.setText("                 ");

If we were to modify this program so that the user would pay per byte downloaded, rather than paying per string, the code in bold font below would be removed, and ring pullouts after download would not stop the ticker.

} // if(received)
while(waitingforscrolltofinish) {
     if(!javaring.isDeviceInserted())
     {
         errlabel = ringisoutlabel;
         showerrlabel = true;
         while(!javaring.isDeviceInserted());
         showerrlabel = false;
     }
}

The code below most likely would be moved inside the for loop, so that the ring would be billed incrementally for each 60-byte block. It's probable that this code will need to be modified when a purse is actually implemented. At present, this loop continually tests the stub purse until it is found to contain enough money to pay for the next download. Again, showErrLabel is used, with a different error message:

while(true) {
     try {
         decrementMoney(rate);
         showerrlabel = false;
         break;
     } catch (OutOfMoneyException e) {
         errlabel = outofmoneylabel;
         showerrlabel = true;
            }
         } // while(true)
     } // while(true)
} // run()

Java Card is real and portability works

Java Card is a standard that allows developers to write programs for smart cards that are portable among different manufacturers' cards. In addition to the advantages brought by portability, because Java Card is actually a language and is a proper subset of Java, Java-trained developers can learn to write Java Card applets relatively quickly. Traditionally, smart card programmers were in-house staff hired by the smart card manufacturers themselves, which meant that smart card programming was essentially equivalent to embedded systems programming. Java Card moves smart card programming away from the card manufacturers to the users. Perhaps users will be more inventive in terms of creating new and more specialized applications for smart cards, which may in turn will bring about an increase in the popularity of the cards as a whole. Additionally, the new portability of Java Card applications may encourage competition among smart card manufacturers, who will no longer be able to claim their card is the only card that provides a given functionality. The Java Card applet associated with our SecureStockViewer was originally tested and run on a Dallas Semiconductor iButton (a Java Ring) and was then ported with minimal-to-no tinkering to a GemXpresso card and a Schlumberger card. You can get your development kits at the Web sites listed in the Resources section.

Conclusion

From a business perspective, it has become obvious to us that multicast is an unexplored and potentially very exciting direction in network applications and applets. Restricted-channel multicast won't be the only multicast out there. With the growth of e-commerce, the need for stream-oriented paid-for content will arise, and developers who are ready to create such applications and applets will be in high demand. I also get the feeling that as e-commerce develops, smart cards and other token devices will pop up over and over again as the most convenient way of regulating payment. Encryption, of course, will guide e-commerce, and a strong background in it will be an absolute requirement for any developer in this area. Still, multicast and token devices will be some of the more important parts of e-commerce enabled by new advances in encryption. As a first-time programmer of a multicast application, I found the JRMS package and the Dallas Semiconductor iButton easy to understand and work with, and I hope I can continue to work on this project or others like it.

Jason Scherer is an intern and first-time Java programmer at Sun Microsystems. He will receive his BA in Computer Science from Columbia University in December of 1998. He has been programming in C for two years and has worked for Lockheed Martin in San Jose and for the Columbia University Applied Physics Department. He is interested in cryptography, user interfaces and graphics, language and compiler design, and mathematics. Hobbies include composing electronic music. He lives in New York with his fiancee Rachel. You can check out his Web site at http://www.columbia.edu/~jps19.

Learn more about this topic