TPM/J Developer's Guide

Current version: 0.3.0 (alpha)
Release date: 3 April 2007

Overview

TPM/J is an object-oriented API using Java for low-level access to the TPM. TPM/J is intentionally not compliant with the TCG's TSS specifications. Its main intent is to provide a flexible object-oriented API that is more immediately usable by researchers and programmers for doing experiments and software R&D, in cases where TCG-compliance is not critical.

TPM/J treats TPM low-level commands (i.e., the commands directly given to the TPM chip itself) and the response data structures of these commands as first-class Java objects. This allows programmers to easily access the fields of the command and response data structures in an object-oriented way, instead of having to explicitly read bytes from command-specific offsets in large byte arrays. Also, because it defines each command and response data structure as a separate Java class, it allows for a more modular, rather than monolithic implementation of APIs for each TPM command.

TPM/J also provides higher-level Java classes that represent higher-level concepts and constructs such as authorization sessions and transport sessions. In the case of authorization sessions, for example, these classes allow a session's state to be kept within a separate session object, instead of putting the burden on the programmer to explicitly create and manage local or global variables for keeping track of such state in his or her own code. In the case of transport sessions, the object-oriented form of the TPM commands allows things such as the encryption and logging of wrapped commands, etc., to be done in a more systematic and modular way (although full support for encrypted wrapped commands is not yet available in the current alpha version.)

Finally, access to the TPM itself is abstracted into a TPM driver object. By providing different platform-specific driver objects for different platforms, we are able to support multiple platforms (e.g., Linux and Windows) without requiring the programmer to change any code. To our knowledge, TPM/J is one of the first cross-platform APIs for using the TPM, that works in both Windows and Linux, without requiring the application programmer to write or compile separate versions.


User's Guide

To use TPM/J on your system, see the User's Guide.


Library and Other settings

See the User's Guide for the basic settings.

Note: in addition to the currently-supported platforms, it should be relatively straightforward to make TPM work with any Windows library which provides a TDDL-level interface. For examples, see the code under src/.../drivers/win32 and the c/IFXTPM and c/TBSProxy directory. If you write your own driver code, please contact us to contribute them back to the community.

Working with the Source Code

The source code is provided in the form of a zipped-up Eclipse project directory. To start working with it, just unzip it to your local file system, and import it as a project into your workspace.

A note on formatting conventions: the TPM/J source code does not follow standard Java formatting conventions. The most noticeable differences are the placement of the opening brace "{", plus a slightly more liberal use of whitespace to improve readability. For a more detailed explanation of the formatting conventions used for TPM/J, see Luis Sarmenta's lecture slides on code formatting . The file SarmentaStyleEclipseCodeFormatterSettings.xml contains Eclipse formatter settings for roughly following the preferred style. Note, though, that Eclipse is not always able to follow all the ideal spacing, line-wrapping, and indentation conventions. In such cases, sticking with what Eclipse produces is acceptable.


High-Level TPM/J programming

The best place to start understanding TPM/J and learning to write your own applications using TPM/J, is to look at the tools classes in the tools package.

For the most part, these do not deal directly with TPM commands, but use the convenience functions in the funcs package. The edu.mit.csail.tpmj.funcs package contains higher-level classes that make it easier to perform commonly-needed tasks without having to deal with the TPM command objects directly. To use these, you generally follow the following steps:

  1. Initialize the TPMDriver. Note that the TPMUtilityFuncs class stores the TPMDriver object in a static variable which is automatically used by the classes in the funcs package.
    TPMUtilityFuncs.initTPMDriver();
    
  2. Call the desired function. Note that the output of these functions are typically the relevant TPM structures themselves, and not the direct output of the command.
    TPM_KEY key = TPMStorageFuncs.TPM_CreateWrapKey(
                    parentHandle, parentAuth, keyAuth, migAuth,
                    keyUsageType, keyUsageFlags );
    System.out.println( "Returned wrapped key: " + key );
    
  3. At the end of your entire program, call TPMUtilityFuncs.cleanupTPMDriver(); This calls the cleanup() method on the driver, which on some platforms (i.e., Windows and Vista) is needed to close the TDDL session/context.

In addition to these functions, there are also generic utility classes provided in edu.mit.csail.tpmj.util. In particular, CryptoUtil has several useful functions for doing some cryptographic computations in software. (Some of these use the Bouncy Castle library for Java.) Also, TPMToolsUtil has convenience methods that are used by the tools classes to make handling command-line options easier.

Low-level TPM/J programming: How to Use TPM Command Objects

In general, the data structures defined by the TCG's Structures of the TPM spec, are defined as separate Java classes in the edu.mit.csail.tpmj.structs package. To be consistent with TCG notation, we have intentionally violated the standard Java naming conventions, and have named the classes in all caps, according to their TCG names. The structs package also contains some classes which do not directly correspond to TCG structures, but which may represent abstract base classes for TCG classes. These classes are not named in all caps.

Similarly, the TPM commands, as defined in the TCG's Commands spec for the TPM are defined as separate Java classes in the edu.mit.csail.tpmj.commands package, again following the same names and capitalization used in the TCG Spec. Additionally, each of these command classes has a corresponding class that defines its output response data structure, with the name <CommandName>Output, where <CommandName> is the name of the command (e.g., TPM_GetPubKey and TPM_GetPubKeyOutput would be the Java class files corresponding to the TPM_GetPubKey command).

To use an existing command object directly, one follows the following steps:
  1. Initialize and get a TPMDriver
    TPMUtilityFuncs.initTPMDriver();
    TPMDriver tpmDriver = TPMUtilityFuncs.getTPMDriver();
    
  2. Create the command object.
    TPM_PCRRead cmd = new TPM_PCRRead( pcrIndex );
    
  3. Call the command's execute method, giving it the TPMDriver object, and receiving the output in the appropriate response type.
    TPM_PCRReadOutput output = cmd.execute( tpmDriver );
    

    Note that this command can throw a TPMException, so it should generally be surrounded by a try-catch. Any errors in executing the command, including TPM errors with a corresponding return code will throw a type of TPMException.
  4. Read and use the output data structure using its fields.
    TPM_PCRVALUE pcrVal = output.getOutDigest();
    System.out.println( "PCR " + pcrIndex + "= " + pcrVal );
    

Using commands which require authorization is more complicated. For these, you would need to use one of the authorization session classes. See edu.mit.csail.tpmj.tests.TPMAuthTest for an example. Note also that there are many "convenience classes" in the edu.mit.csail.tpmj.funcs package for hiding the complexity of using authorization sessions directly for commonly-used tasks.

How to Create Your Own TPM Command Objects

At present, TPM/J does not yet implement command objects for all TPM 1.2 ordinals. However, TPM/J is designed to easily accommodate new command ordinals in the future. To create support for an unsupported command, you generally have to follow the following steps:
  1. Check to see the TPM structures used in the input and output structures of the command. Create the corresponding Java files for these in the structs package, as necessary. The key tasks in defining these class files is writing the fields, the constructor, the toBytes method, and the fromBytes method. Look at the source code in the structs package for examples.
  2. Create the command class under the commands package, under the same name as given by TCG. It should be descended from TPMCommand (commands that require authorization should extend one of the TPMAuth* classes or TPMKeyAuth1Command). Note that TPMCommand classes are themselves TPMStructs, so you must also define the toBytes and fromBytes methods to specify how the command is converted into a byte array.
  3. Create the corresponding <CommandName>Output class. This should be placed in the commands package (not the structs package).
  4. Override the execute(...) method(s) of the <CommandName> class to return the appropriate type. e.g.,
        @Override
        public TPM_PCRReadOutput execute( TPMDriver tpmDriver ) throws TPMException
        {
            return (TPM_PCRReadOutput) super.execute( tpmDriver );
        }
    
  5. Also override the getReturnType() method of the <CommandName> class to return the class of the appropriate type. e.g.,
        @Override
        public Class getReturnType()
        {
            return TPM_PCRReadOutput.class;
        }
    
  6. If desired, add corresponding convenience functions for using the new command to one of the classes (or a new class) in the funcs package. These convenience functions should hide the creation of the TPM command object itself. It should receive the user-level input data for the command, and return the desired TPM structure. (Although in cases where the TPM returns several data structures of interest, it is OK, or even better, to return the entire command output data structure itself directly.)

See the source code in the commands package for examples.

(TODO: More explicit documentation on how to define ByteArrayables and write toBytes and fromBytes methods. For now, please look at the examples in the source code.)

Parameter Handling

The tools in the tools package use a package for handling command-line parameters from my (Luis Sarmenta's) thesis project Bayanihan. It provides a convenient way to handle command-line parameters and switches. To understand how it works, look at the source code and the tools package.

Debugging

The edu.mit.csail.tpmj.util.Debug class provides debugging functions in the form of print statements that only print when debugging for certain classes is on. To use, add Debug.setDebugOn( MyClass.class, true ) for each class "MyClass" that you want to debug, at the beginning of the main() method of the main class you are running. Then, in MyClass, you can use Debug.print( ... ) and Debug.println( ... ) methods for conditional debugging output. If debugging has been turned on for MyClass, then these methods will print to System.out. Otherwise, they won't. See the demos, tools, and tests classes, and the classes that these debug, for examples.

Note that the print and println commands accept a variable-length argument list of Objects. The recommended way to use it is to use a comma (',') instead of string concatenation ('+') when printing a complex object (e.g., a TPMStruct object) with a long toString(...) method. This way the call to the toString of the object is delayed until within the method itself, and only happens if debugging is on. This can save a significant amount of time when debugging is off.

For example,
The following:
Debug.println( "output = ", outputStruct );
is better than:
Debug.println( "output = " + outputStruct );
(Note also that byte array objects are automatically printed using ByteArrayUtil.toPrintableHexString(...) within the print statement.)

The main() method of the Debug class has a demonstration of the performance differences of these two techniques.

For more complex conditional Debug operations, you can also use an if statement with the Debug.isDebugOn() method.

Finally, most of the tools in the tools package support the /D option, which turns on debugging mode for the tpm driver. This prints all the input and output bytes to/from the TPM.

To-Do List

Unresolved/Unexplained problems

Others

  1. Not all TPM commands have corresponding command classes yet. Continue to implement unimplemented ordinals.
  2. Currently, transport sessions work enough to be usable for signing monotonic counter operation outputs. (This is the only way to get non-forgeable signed outputs certifying the value of the monotonic counters). At present, however, transport sessions do not fully support encryption. They also do not support all TPM Commands. (Some ordinals which require special HMAC handling may or may not work properly right now.)
  3. NVRAM functions are working but not fully tested.
  4. AIK creation creates usable AIKs, but has not been tested with real privacy CAs.
  5. Javadoc documentation should be cleaned-up and produced.
  6. Some of the examples can be improved or documented better. There are some tests or main functions in the funcs package, which can/should eventually be turned into independent tools classes.

Timing Tests

Use demo.TPMTiming to view a timing report for various TPM functions. Use tests.TPMCounterTimingTest to view timing constraints for monotonic counters.
java edu.mit.csail.tpmj.tests.TPMTiming <ownerPwd> [srkPwd]
Notes:

Other (Old) Tests

The code in the tpmj.tests folder is rough code intended to test certain features of TPM/J as well as give developers an idea of ways to use these features at a lower level. Note also that some of the classes in the tpmj.funcs folder contain their test code in their main() methods.

(Note:The documentation for these tests have not been updated since version 0.2.0, and the tests themselves may not have been fully tested since then. Some the tests below may not work anymore.)

Test running TPM commands in OIAP and OSAP sessions

Use tests.TPMAuthTest to test runnning TPM commands in OIAP and OSAP sessions:
java edu.mit.csail.tpmj.tests.TPMAuthTest <keyHandle>
Arguments:
- keyHandle - The key handle (in hex) of the key loaded in the TPM that should be used to perform this test. Defaults to 0x40000000 (SRK).

Notes:
- The final test ("Trying OIAPSession with wrong secret") should fail with a TPM_AUTHFAIL

Test the PCR quoting functionality of the TPM

Use tests.TPMPcrTest to test the PCR quoting functionality of the TPM:
java edu.mit.csail.tpmj.tests.TPMPcrTest <keyFile> <keyPassword>
Arguments:
- keyFile - A TPM key capable of signing data. This will default to 'testkey.key'.
- keyPassword - The password to the specified TPM key. This will default to 'test'.

Notes:
- The 'signing' attack mentioned at the end of the test demonstrates how a non-AIK signing key can be used to both a quote or TPM sign arbitrary data, producing identical signatures. This shows that signing a TPM_Quote operation is not secure if a key other an AIK is used.

Test a saved key

Use tests.TPMKeyTest to test a saved .key file generated by TPMCreateKey or TPMWrapKey:
java edu.mit.csail.tpmj.tests.TPMPcrTest <keyFile> <keyPassword> <parentPassword>
Arguments:
- keyFile - A TPM key capable of signing data. This will default to 'testkey.key'.
- keyPassword - The password to the specified TPM key. This will default to (no authorization required).
- parentPassword - The password of the parent key for loading the key.  This will default to all-zeros or no authorization.

Notes:
- The tests done here are similar to those done in TPMCreateKey and TPMWrapKey but work on an existing key (without creating a new one).
- If keyFile has a corresponding .ser file, it will be loaded and deserialized as an RSA keypair.  The crypto operations would then be done both in software and using the TPM, and the results compared.

Test the functionality of a monotonic counter

Use tests.TPMCounterTest to test the functionality of the TPM's monotonic counters:
java edu.mit.csail.tpmj.tests.TPMCounterTest <counterID>
Arguments:
- counterID - The monotonic counter that should be used in this test.

Notes:
- Only one monotonic counter on the TPM can be incremented per reboot. If any other monotonic counters have been incremented since the last reboot, this test will fail.

Test the timing constraints of a monotonic counter

Use tests.TPMCounterTimingTest to test the timing constraints of the TPM's monotonic counters:
java edu.mit.csail.tpmj.tests.TPMCounterTimingTest <counterID>
Arguments:
- counterID - The monotonic counter that should be used in this test.

Notes:
- Only one monotonic counter on the TPM can be incremented per reboot. If any other monotonic counters have been incremented since the last reboot, this test will fail.
- This test needs to load a key named 'testaik.key' with password 'test' from the current working directory.
- This test will take a while, since it performs 100 increments on the specified counter.

Test the functionality of the transport sessions

Use tests.TPMTransTest to see how counters can be incremented from within a transport session in the TPM:
java edu.mit.csail.tpmj.tests.TPMTransTest <counterID>
Arguments:
- counterID - The counter that will be used in this transport session test.

Notes:
- This test needs to load a key named 'testkey.key' with password 'test' from the current working directory.
- The specified counter will be incremented during this test.
- Again, because of the 'signing attack', an AIK should be used for signing transport sessions logs. Otherwise, an adversary can easily fake a log and use TPM_Sign to produce the appropriate signature.

Contributors


Original author: Luis Sarmenta (lfgs at mit dot edu)
Last edited by: Luis Sarmenta (lfgs at mit dot edu) on April 3, 2007